# 国密协处理器固件(CUni360S-Z) ## TL;DR > **Quick Summary**: 基于 CUni360S-Z 芯片构建国密(SM2/SM3/SM4)协处理器固件,通过 SPI Slave 接口挂载到嵌入式 Linux 主机。芯片内部生成 SM2 密钥对(私钥永不出片),对外暴露自定义二进制协议,支持全量国密运算 + X.509 证书生成。 > > **Deliverables**: > - SPI Slave 传输层(DMA LLI + 中断驱动) > - 自定义二进制帧协议(SYNC+LEN+CMD+PAYLOAD+CRC16) > - SM2 服务(密钥生成/签名/验签/加密/解密/密钥协商) > - SM3 服务(流式哈希) > - SM4 服务(ECB/CBC/流式加解密) > - X.509 自签名证书生成 > - eFlash 密钥安全存储 > - TDD 测试向量全覆盖 > > **Estimated Effort**: Large(~12 天等效,5 个并行 Wave) > **Parallel Execution**: YES — 5 waves > **Critical Path**: 验证 → 传输 → 协议 → 服务 → 应用集成 --- ## Context ### Original Request 基于 CUni360S-Z 芯片制作国密卡,支持 SM2/SM3/SM4 加解密,通过 SPI 接口连接嵌入式 Linux 主机。 ### Interview Summary **Key Discussions**: - **物理接口**: SPI Slave(SPI1 或 SPI2,Linux 主机为 Master)。SPI3 被 NOR Flash 占用。 - **协议**: 自定义精简二进制协议。帧格式:SYNC(0xAA55) + LEN(2B) + CMD(1B) + PAYLOAD(N bytes) + CRC16(2B),应答带 STATUS 字节。 - **密钥管理**: SM2 密钥对由芯片内 TRNG 生成,私钥永不出芯片(eFlash 存储),仅导出公钥。SM4 密钥由主机动态注入(RAM),不持久化。 - **密钥槽数量**: 1 组 SM2 密钥对。 - **支持操作**: SM2 全量(签名/验签/加密/解密/密钥协商)、SM3 流式哈希、SM4 ECB/CBC/流式文件加解密。 - **证书**: 芯片内生成 SM2 自签名 X.509 证书(Base64 编码),通过 SPI 导出。 - **测试策略**: TDD(测试驱动开发),使用 SM2/SM3/SM4 国密标准测试向量。 **Research Findings** (codebase exploration): - SDK 已有完整硬件 API:`alg_drv.h`(SM2/3/4 Gen2 `_2` API)、`spi_drv.h`(SPI 驱动)、`eflash_drv.h`(eFlash)、`crc_drv.h`(CRC)、`cert/x509.h`(X.509 证书生成) - `spi_demo.c:98-124` 有 SPI DMA LLI 从机接收的可用模板 - `dma_spi_LLIReceive()` 已配置 DMA 中断,只需接入 ISR 即可实现异步接收 - SPI1+SPI2 共享 IRQ3(ISR23),需要手写共享中断分发器 - `dma_spitran()` 的 `binten` 参数被硬编码为 FALSE,中断分支是死代码,必须使用 LLI 路径 ### Metis Review **Identified Gaps** (addressed): - **SPI1/2 共享中断**: 已纳入 Task 4,手写 IRQ3 分发器 - **vector_table.h 编译期绑定**: ISR 绑定需要编辑头文件并重编译 — 已纳入 Task 3 - **dmac_isr vs DMA_IRQHandler 命名不匹配**: 已纳入 Task 1 验证步骤 - **RAM 预算紧张**(64KB 总量,密码运算需 3.5KB+): 已纳入 Task 2 RAM 审计 - **SPI 8 字节 FIFO 溢出风险**: 使用 DMA LLI 消除风险(Task 3) - **SM4 无硬件流式 CBC 状态**: 软件 IV 包装器(Task 10) - **eFlash 无单页锁定**: 地址范围校验护栏(Task 11) - **while(1) 空转功耗**: 纳入 Phase 2 优化(非 MVP 必须) --- ## Work Objectives ### Core Objective 构建一个完整的国密协处理器固件:Linux 主机通过 SPI 发送二进制命令帧 → CUni360S-Z 接收、解析、执行密码运算 → 返回结果帧。所有 SM2 私钥操作在芯片内部完成,私钥永不出芯片。 ### Concrete Deliverables - `src/gmsp/transport/spi_slave.c/.h` — SPI DMA LLI 从机传输层 - `src/gmsp/protocol/gmsp_frame.c/.h` — 帧解析/封装 + CRC16 校验 - `src/gmsp/service/sm2_svc.c/.h` — SM2 服务封装(含测试向量) - `src/gmsp/service/sm3_svc.c/.h` — SM3 服务封装(含测试向量) - `src/gmsp/service/sm4_svc.c/.h` — SM4 服务封装(含测试向量) - `src/gmsp/service/keystore.c/.h` — eFlash 密钥安全存储 - `src/gmsp/service/cert_svc.c/.h` — X.509 证书生成服务 - `src/gmsp/app/gmsp_dispatch.c/.h` — 命令分发器 - `include/vector_table.h` — 更新 ISR23 绑定 - `src/gmsp/test/*_test.c` — TDD 测试文件 - `src/main.c` — 更新启动序列 + 主循环 ### Definition of Done - [ ] 所有测试向量通过:`bun test`(或等效)→ 全部 PASS - [ ] Linux 主机通过 SPI 发送 `0x02`(SM2 签名)命令 → 芯片返回正确签名 - [ ] Linux 主机通过 SPI 发送 `0x31`(生成证书)命令 → 芯片返回有效 Base64 X.509 证书 - [ ] SPI 传输速率 ≥ 1 MB/s(DMA LLI 模式) - [ ] SM2 私钥从芯片 eFlash 读出验证失败(固件无此命令) ### Must Have - SPI Slave DMA LLI 异步接收(不丢帧,≥1 MB/s) - 自定义二进制帧协议(SYNC 同步 + CRC16 校验 + 错误应答) - SM2 密钥生成 + 签名 + 验签 + 加密 + 解密 + 密钥协商 - SM3 流式哈希 - SM4 ECB/CBC + 流式分片加解密 - X.509 自签名证书生成 - eFlash 密钥安全存储(地址范围校验防护) - 每个模块对应 TDD 测试向量 ### Must NOT Have (Guardrails) - **NO ISO 7816 / ICC 智能卡协议** — 不走 APDU 框架 - **NO USB 接口** — 不使用 USB CCID/CDC/HID/UMS - **NO 多密钥槽** — 仅 1 组 SM2 密钥对 - **NO PKCS#7** — 仅 X.509 自签名证书 - **NO RTOS** — 保持裸机 while(1) 架构 - **NO `dma_spitran()` with interrupts** — 中断分支是死代码,必须用 LLI 路径 - **NO 假设 eFlash 页可单独锁定** — 必须用地址范围校验防护误写 --- ## Verification Strategy > **ZERO HUMAN INTERVENTION** — 所有验证为 Agent 执行。验收标准中"人工确认/手动测试"不允许。 ### Test Decision - **Infrastructure exists**: NO(无标准 C 测试框架可用,CCore 交叉编译环境) - **Automated tests**: TDD(测试向量驱动的自检模式) - **Framework**: 芯片内自检(上电运行测试向量 → 通过 UART 输出 PASS/FAIL) - **TDD 流程**: 每个模块编写测试向量 → 实现功能 → 芯片上运行验证 → UART 输出结果 ### QA Policy 每个任务必须包含 Agent-Executed QA Scenarios。证据保存至 `.sisyphus/evidence/task-{N}-{scenario-slug}.{ext}`。 - **编译验证**: 检查 `.elf` 文件生成成功,无编译错误/警告 - **功能验证**: 读取 UART 输出确认 TEST PASS - **静态分析**: 搜索禁止模式(`dma_spitran` with interrupts、未使能时钟的 crypto 调用等) --- ## Git Strategy ### Remote Repository - **URL**: `http://gogs.weclouds.xyz:3000/kennyh/encryption_sm.git` - **Branch**: `main` - **Protocol**: 每个任务完成并通过验收后 → `git add -A && git commit -m "type(scope): desc"` → `git push origin main` ### Commit & Push Policy - 每个任务的 `**Commit**` 段定义了提交信息 - 提交后**立即 `git push origin main`**,不累积 - 如果 push 失败(网络/认证)→ 记录到 `.sisyphus/evidence/task-N-push-fail.txt`,不阻塞后续任务 - 首次提交前需先完成 Task 0(Git 仓库初始化) --- ## Execution Strategy ### Parallel Execution Waves ``` Wave -1 (Git 初始化 — 1 task, 必须最先完成): └── Task 0: Git 仓库初始化 + Remote 配置 Wave 0 (验证基础 — 2 tasks, 可并行): ├── Task 1: 验证 dmac_isr 绑定 + SPI1 引脚确认 └── Task 2: RAM 预算审计 + eFlash 存储区域确认 Wave 1 (传输层 + 协议层基础 — 4 tasks, 可并行): ├── Task 3: SPI DMA LLI 传输层(核心) ├── Task 4: IRQ3 共享中断分发器 ├── Task 5: CRC16 帧校验模块 └── Task 6: 帧协议(解析+封装) Wave 2 (密码服务层 — 5 tasks, 最大化并行): ├── Task 7: SM2 服务 + 测试向量 ├── Task 8: SM3 服务 + 测试向量 ├── Task 9: SM4 ECB/CBC 服务 + 测试向量 ├── Task 10: SM4 流式加解密 + 软件 IV 包装器 + 测试向量 └── Task 11: eFlash 密钥存储服务 + 安全护栏 Wave 3 (应用层 + 集成 — 4 tasks, 可并行): ├── Task 12: 命令分发器(CMD → handler 路由) ├── Task 13: X.509 证书生成服务 + 测试 ├── Task 14: main.c 集成(启动序列 + 主循环) └── Task 15: 主机侧 SPI 测试工具(Linux 端) Wave FINAL (验证 — 4 tasks, 并行): ├── Task F1: 全量测试向量回归 ├── Task F2: SPI 吞吐量测试(≥1 MB/s) ├── Task F3: 安全审计(私钥不出芯片 + 边界检测) └── Task F4: 代码质量审查 ``` **Critical Path**: Task 0 → Task 1 → Task 3 → Task 6 → Task 7 → Task 12 → Task 14 → F1-F4 **Parallel Speedup**: ~65% faster than sequential (excluding Task 0 which must be first) **Max Concurrent**: 5 (Wave 2) **Git Push**: 每个任务完成后 `git push origin main` --- ## TODOs - [x] 0. Git 仓库初始化 + Remote 推送配置 **What to do**: - 在项目根目录执行 `git init` - 创建 `.gitignore`:忽略 `Debug/`(编译产物)、`.metadata/`(Eclipse 工作区)、`*.o`、`*.d`、`*.elf`、`*.bin`、`*.map`、`*.a` - 执行 `git add -A && git commit -m "chore: initial commit — CUni360S-Z Demo V3.4 SDK baseline"` - 添加远程仓库:`git remote add origin http://gogs.weclouds.xyz:3000/kennyh/encryption_sm.git` - 推送:`git push -u origin main`(如果远程仓库为空直接推送;如果已有内容则 `git pull --rebase origin main` 后再推送) - 配置 Git 用户信息(如果未配置):`git config user.name "kennyh"` 和 `git config user.email` **Must NOT do**: - 不要将 `Debug/` 目录(含 `.o`、`.elf`、`.bin`、`.map`、`.a`)提交到仓库 - 不要将 `.metadata/` 提交到仓库 - 不要将本地 Eclipse 配置提交 **References**: - `.project/` — Eclipse 项目描述(应保留,不含敏感信息) - `linkmap` — 链接脚本(应保留) - `gdbinit` — GDB 配置(应保留) **Acceptance Criteria**: - [ ] `git log --oneline -1` 显示初始提交 - [ ] `git remote -v` 显示 `origin http://gogs.weclouds.xyz:3000/kennyh/encryption_sm.git` - [ ] `git ls-files Debug/` 返回空(已排除编译产物) **QA Scenarios** (Agent-Executed): ``` Scenario: Git 仓库初始化与推送 Tool: Bash (git 命令) Steps: 1. 在项目根目录执行 git init 2. 创建 .gitignore 排除 Debug/ 和 .metadata/ 3. git add -A && git commit -m "chore: initial commit" 4. git remote add origin http://gogs.weclouds.xyz:3000/kennyh/encryption_sm.git 5. git push -u origin main Expected Result: 远程仓库可见初始提交 Failure Indicators: push 失败、.gitignore 缺失 Evidence: .sisyphus/evidence/task-0-git-init.txt ``` **Commit**: YES (initial commit) - Message: `chore: initial commit — CUni360S-Z Demo V3.4 SDK baseline` - Files: 全部源码(除 Debug/ 和 .metadata/ 外) - Pre-commit: 无 - [x] 1. 验证 dmac_isr 绑定 + SPI1 引脚确认 **What to do**: - 检查 `Debug/*.map` 文件,确认 `dmac_isr` 是否被绑定到 IRQ4(`DMA_IRQHandler` 符号) - 如果符号不匹配 → 修复 `dmac_drv.c` 中的函数名或在 `vector_table.h` 中使用正确的符号名 - **SPI1 引脚确认**: 确认 `SCK1(SPIDDR_SCK)`, `SS1(SPIDDR_SS)`, `MISO1(SPIDDR_MISO)`, `MOSI1(SPIDDR_MOSI)` 未被其他功能占用 - SPI1 基地址 `0x70000000`,spiid=0(`dma_spi_LLIReceive` 用 `(spiid<<16)` 计算地址偏移) - 确认 CRC16 硬件模块(CRC0/CRC1)可用,或确认 `crc_drv.h` 中 CRC16-CCITT 是否匹配 **Must NOT do**: - 不要跳过 .map 文件验证 — 如果 dmac_isr 未正确绑定,整个异步 SPI 架构不工作 **References**: - `Debug/CUni360S-Z_Demo.map` — 搜索 `dmac_isr` 和 `DMA_IRQHandler` 符号 - `src/ccore/vector_table.c` — ISR 向量表定义 - `include/vector_table.h` — ISR 符号声明 - `src/drv/dmac_drv.c` — `dmac_isr()` 实现 - `src/drv/inc/spi_drv.h` — SPI 引脚/通道定义 - `src/drv/inc/crc_drv.h` — CRC 硬件 API **Acceptance Criteria**: - [ ] `.map` 文件中 `dmac_isr` 出现在 IRQ4 对应位置(或已修复) - [ ] SPI1/SPI2 引脚确认可用(文档记录选择) **QA Scenarios** (Agent-Executed): ``` Scenario: .map 文件符号验证 Tool: Bash (grep in PowerShell) Steps: 1. grep "dmac_isr" Debug/CUni360S-Z_Demo.map — 确认符号存在 2. grep "DMA_IRQHandler" Debug/CUni360S-Z_Demo.map — 确认入口地址 3. 对比两个符号的地址是否一致 Expected Result: 两个符号解析到同一地址(或已重命名一致) Evidence: .sisyphus/evidence/task-1-map-verify.txt ``` **Commit**: YES - Message: `chore(gmsp): verify dmac_isr binding and SPI pin availability` - Files: 无代码变更(或修复 dmac_drv.c 命名) - Pre-commit: 无 - [x] 2. RAM 预算审计 + eFlash 存储区域确认 **What to do**: - 从 `.map` 文件中提取当前 RAM 使用量(`.data` + `.bss` 总和) - 计算密码运算所需 RAM:SM2 运算需要 ~1KB 上下文 + SM4 流式缓冲区 ~4KB + SPI 帧缓冲 ~2KB + 通用开销 ~2KB = ~9KB - 确认 64KB RAM 余量充足(当前使用量 + 9KB < 55KB) - 在 `linkmap` 中确认 eFlash 末尾区域(`0x00470000` 以上)未被 `.text` 占用 - 如果 eFlash 区域不足,调整 `_end_text` 或缩小代码段 **Must NOT do**: - 不要假设 RAM 充足而不验证 — 64KB 在密码运算场景下很紧张 - 不要使用动态 malloc — 全部静态分配全局缓冲区 **References**: - `Debug/CUni360S-Z_Demo.map` — `.data`/`.bss` 段大小 - `linkmap` — eFlash/RAM 内存布局 - `src/drv/inc/alg_drv.h:12-16` — SRAM_BEGIN/END, EFLASH_BEGIN/END - `src/main.c` — 当前初始化序列中的全局变量 **Acceptance Criteria**: - [ ] RAM 余量 ≥ 15KB(64KB - 当前使用量 - 9KB 密码开销) - [ ] eFlash `0x00470000-0x0047F000`(60KB)可用于密钥存储 **QA Scenarios** (Agent-Executed): ``` Scenario: RAM 预算审计 Tool: Bash (PowerShell) Steps: 1. 从 .map 文件提取 _bss_end 地址 2. 计算 RAM 使用量 = _bss_end - 0x00800000 3. 确认 0x00800000 + 使用量 + 15000 < 0x00810000 Expected Result: 确认至少 15KB RAM 余量 Evidence: .sisyphus/evidence/task-2-ram-audit.txt ``` **Commit**: YES - Message: `chore(gmsp): RAM budget audit and eFlash key region reservation` - Files: `linkmap`(如需要调整区域) - Pre-commit: 编译验证 - [x] 3. SPI1 DMA LLI 传输层(Ping-Pong 双缓冲 + 中断驱动) **What to do**: - 创建 `src/gmsp/transport/spi_slave.c/.h` - **SPI1 硬件配置**: - 引脚:`SCK1(SPIDDR_SCK)`, `SS1(SPIDDR_SS)`, `MISO1(SPIDDR_MISO)`, `MOSI1(SPIDDR_MOSI)` - 模式:`SPI_Mode_Slave`(SPICR1.MSTR=0) - 参数:CPOL=Low, CPHA=1Edge, 8-bit data (`SPI_DataSize_8b`), MSB first - SPI1 基地址 `0x70000000`,spiid=0 - **DMA LLI 接收**(基于 `spi_demo.c:98-124` 模板): - 使用 `dma_spi_LLIReceive(DMACCH1, 0, buf, BUF_SIZE)` 配置 DMACCH1 为 SPI RX - LLI `next_lli` 指向自身 → 自动循环接收(无需 CPU 介入持续收数据) - `DMA_IE` 已内置置位 → 每次传输完成触发 `dmac_isr()` → 设置 `volatile dma_isr_flag` - **Ping-Pong 双缓冲**: - 分配两个接收缓冲区 `gmsp_buf_A[256]` 和 `gmsp_buf_B[256]` - LLI 交替指向 buf_A / buf_B - `dmac_isr()` 中:切换缓冲区指针 → 设置 `gmsp_recv_flag = 1` → 前台处理 - **SPI 发送(响应路径)**: - 短响应使用 `SPI_SlaveSendData(SPI1, resp, len)`(CPU 写入 FIFO) - 长响应(>64 字节)使用 `dma_spitran(0, resp, dummy, len, FALSE)`(DMACCH0 全双工,阻塞等待完成) - 暴露 API:`gmsp_transport_init()`, `gmsp_recv_ready()`, `gmsp_get_rx_buf()`, `gmsp_send_response(resp, len)` **Must NOT do**: - 不要使用 `dma_spitran()` 的中断模式 — `binten` 被硬编码为 FALSE(`dmac_drv.c:125`),中断分支是死代码 - 不要使用 CPU 轮询 `SPI_SlaveReceiveData()` 收大量数据 — SPI FIFO 仅 8 字节,高波特率下必丢数据 - 不要使用 SPI3 — 被 NOR Flash 占用(SPI3_BASE_ADDR `0x70020000`) - 不要忘记 `dma_spi_LLIReceive` 前调用 `SPI_EnableDMA(SPI1, TRUE)` 使能 SPI DMA 通路 **References**: - `src/demo/spi_demo.c:62-124` — **完整从机模板**:SPI1 初始化为 Slave + DMA LLI 自循环接收 + `dma_getLength()` 读字节数 - `src/drv/dmac_drv.c:195-222` — `dma_spi_LLIReceive()` 实现:LLI `src=SPI1_BASE_ADDR+0x12(SPIDR)`, `dst=buf`, `control0=DMA_IE|SNC|DI|LLP_DST_EN|LLP_SRC_EN|P2M_DMA`, `next_lli=&g_dma_lli_rx` 自循环 - `src/drv/dmac_drv.c:31-46` — `dmac_isr()` 实现:读 `DMA_STATTFR` → 设置 `dma_isr_flag` → 清除 `DMA_CLRTFR` - `src/drv/dmac_drv.c:123-182` — `dma_spitran()` 实现:DMACCH0=TX(M2P_DMA, `SPI1_BASE_ADDR+0x12`), DMACCH1=RX(P2M_DMA) - `src/drv/dmac_drv.c:252-259` — `dma_getLength(n)` = `当前 DADDR - 原始 dst_addr`(已收字节数) - `src/drv/inc/spi_drv.h:27-35,44-56` — `SPI_PINx` 枚举, `SPI1/SPI2/SPI3` 宏, `SPI_Mode_Slave=0x00` - `src/drv/inc/spi_reg.h:13-60` — SPI 寄存器结构体(`SPIDR=0x12` 数据寄存器, `SPISR=0x16` 状态寄存器) - `src/drv/inc/dmac_drv.h:17-30` — DMACCH0/1/2/3 通道号定义, `dma_channel_1=0..dma_channel_4=3` 枚举 - `src/drv/inc/dmac_reg.h` — LLI 结构体 `DMA_LLI {src_addr, dst_addr, next_lli, control0, control1, len}` **Acceptance Criteria**: - [ ] 编译无错误 - [ ] SPI1 Slave 初始化后,Linux 主机发送测试帧 → `dmac_isr` 触发 → `gmsp_recv_flag` 置位 - [ ] Ping-Pong 双缓冲切换正确:连续收到 3 帧无数据覆盖/丢失 - [ ] 响应路径:`gmsp_send_response()` 通过 SPI1 正确回传数据给主机 **QA Scenarios** (Agent-Executed): ``` Scenario: SPI1 从机基本接收(DMA LLI + dmac_isr) Tool: UART 输出分析 Preconditions: 芯片上电,Linux 主机 SPI Master 发送帧 Steps: 1. 编译固件并烧录 2. 主机通过 SPI1 发送: SYNC=0xAA 0x55 LEN=0x0008 CMD=0xF0 PAYLOAD=0x00000000 [CRC] 3. 芯片 UART 输出: "dmac_isr_flag=02" → "gmsp rx: 10 bytes on buf_A" 4. 主机再发第二帧 → UART: "buf switch: A→B" → "gmsp rx: 10 bytes on buf_B" Expected Result: DMA 中断触发 + 双缓冲切换正确 + 两帧完整接收 Failure Indicators: 超时无中断 / 单帧数据不完整 / buf_B 覆盖了 buf_A 的数据 Evidence: .sisyphus/evidence/task-3-spi1-dma-pingpong.txt ``` **Commit**: YES - Message: `feat(gmsp): SPI DMA LLI slave transport layer` - Files: `src/gmsp/transport/spi_slave.c`, `src/gmsp/transport/spi_slave.h` - Pre-commit: 编译通过 - [x] 4. IRQ3 共享中断分发器(SPI1 + SPI2 + PIT32) **What to do**: - 编辑 `include/vector_table.h`:将 `ISR23` 绑定到 `gmsp_irq3_handler` - 写 `gmsp_irq3_handler()`:读 SPI1 状态寄存器 `SPI1->SPISR` → 识别 `SPISR_SPIF_MASK`(传输完成) → 调用 SPI 传输层回调 - **同样检查 SPI2 和 PIT32 的状态** — 三者共享 IRQ3,不能丢失其他外设的中断 - SPI1 中断条件:`DMA_MASKTFR` 触发 → `dmac_isr()` → 我们在 `dmac_isr` 中设置 `gmsp_recv_flag`(不需要在 IRQ3 中处理 SPI) - **关键理解**:IRQ3 处理 SPI1/SPI2 的**非 DMA 中断**(如 FIFO 错误、模式错误);DMA 完成中断走 IRQ4(`dmac_isr`) **Must NOT do**: - 不要假设只有一个中断源触发 — 必须轮询所有共享外设的状态寄存器 - 不要跳过 PIT32 状态检查 — 丢失 PIT32 中断会导致定时器失效 - 不要在 ISR 中做复杂处理 — 仅设置标志,前台处理 **References**: - `include/vector_table.h:25-30` — `ISR23` 当前绑定(`PIT32_SPI1_SPI2_ISR`) - `include/interrupt.h:36-44` — IRQ 优先级配置 + `Enable_Interrupts` 宏 - `src/drv/inc/spi_reg.h` — SPI 状态寄存器位定义(`SPISR_RFNE_MASK` 等) - `src/drv/inc/pit32_drv.h` — PIT32 中断标志 - `AGENTS.md` — anti-pattern: "IO/CLK/RST must be 0 before card VCC" **Acceptance Criteria**: - [ ] ISR23 编译绑定到 `gmsp_irq3_handler` - [ ] SPI 接收中断被正确识别并设置标志 - [ ] PIT32 定时器中断不被 SPI 处理干扰 **QA Scenarios** (Agent-Executed): ``` Scenario: IRQ3 分发验证 Tool: UART 输出分析 Preconditions: SPI 和 PIT32 均已初始化 Steps: 1. 主机发送 SPI 帧触发 SPI 中断 2. 同时 PIT32 定时器到期触发 PIT32 中断 3. 固件在 gmsp_irq3_handler 中打印中断源: "IRQ3: SPI_RX" 或 "IRQ3: PIT32" 4. 确认两种中断都被正确识别和分发 Expected Result: UART 输出显示两种中断源都被处理 Failure Indicators: 只有一种中断被处理 / 中断丢失导致系统停止 Evidence: .sisyphus/evidence/task-4-irq3-dispatch.txt ``` **Commit**: YES - Message: `feat(gmsp): IRQ3 shared interrupt dispatcher for SPI+PIT32` - Files: `include/vector_table.h`, `src/gmsp/transport/gmsp_isr.c` - Pre-commit: 编译通过 - [ ] 5. CRC16 帧校验模块 **What to do**: - 创建 `src/gmsp/protocol/gmsp_crc.c/.h` - 实现 CRC16-CCITT(多项式 `0x1021`,初始值 `0xFFFF`) - 优先使用硬件 CRC 模块(`CRC0_BASE_ADDR 0x78000000` 或 `CRC1_BASE_ADDR 0x7c000000`) - 如果硬件 CRC 多项式不匹配 → 软件查表法实现 - 暴露 API:`gmsp_crc16_init()`, `gmsp_crc16_update(data, len)`, `gmsp_crc16_final()` - 编写 CRC 测试向量:已知输入 → 已知 CRC16 输出 **Must NOT do**: - 不要假设硬件 CRC 多项式匹配 CRC16-CCITT — 必须验证 - 不要跳过 CRC 测试向量验证 **References**: - `src/drv/inc/crc_drv.h` — CRC 硬件 API(`CRC_ResetDR()`, `CRC_CalcCRC()`) - `include/memmap.h:48-49` — CRC0_BASE_ADDR, CRC1_BASE_ADDR - `src/demo/crc_demo.c` — CRC 硬件使用示例 **Acceptance Criteria**: - [ ] CRC16-CCITT 测试向量通过(例如:`"123456789"` → `0x29B1`) - [ ] CRC 校验拒绝损坏帧(修改 1 字节 → CRC 不匹配) **QA Scenarios** (Agent-Executed): ``` Scenario: CRC16 测试向量验证 Tool: UART 输出分析 Preconditions: 固件编译并烧录 Steps: 1. 固件上电后运行 CRC16 自检: "CRC16 test: 123456789" 2. 比对 UART 输出: "CRC16=0x29B1, expected=0x29B1 → PASS" Expected Result: UART 显示 PASS Failure Indicators: CRC 值不匹配 / 固件死循环 Evidence: .sisyphus/evidence/task-5-crc16-test.txt Scenario: CRC 损坏帧检测 Tool: UART 输出分析 Steps: 1. 主机发送正确帧 → 固件返回 ACK 2. 主机发送 CRC 错误的帧(修改 CRC 字段) → 固件返回 NAK Expected Result: 正确帧通过,损坏帧被拒绝 Evidence: .sisyphus/evidence/task-5-crc-reject.txt ``` **Commit**: YES - Message: `feat(gmsp): CRC16-CCITT frame validation module` - Files: `src/gmsp/protocol/gmsp_crc.c`, `src/gmsp/protocol/gmsp_crc.h` - Pre-commit: 编译通过 - [ ] 6. 帧协议(解析+封装) **What to do**: - 创建 `src/gmsp/protocol/gmsp_frame.c/.h` - 参照 `.sisyphus/drafts/gmsp-protocol.md` 实现完整的帧协议 - 实现帧解析:扫描 SYNC(0xAA55) → 读 LEN(2B, 大端) → 读 CMD(1B) → 读 PAYLOAD(N bytes) → 验证 CRC16(2B) - 实现帧封装:填入 SYNC → 填入 LEN → 填入 STATUS → 填入 RESPONSE → 计算 CRC16 → 发送 - 支持超时处理:如果在 LEN 指定时间内未收到完整帧 → 复位解析状态机 - 状态机:`IDLE → SYNC1 → SYNC2 → LEN_LO → LEN_HI → CMD → PAYLOAD → CRC_LO → CRC_HI → DONE` - 暴露 API:`gmsp_frame_parse()`, `gmsp_frame_build_response(cmd, status, resp, len)`, `gmsp_frame_reset()` - 在 `gmsp_frame.h` 中定义所有命令码常量和 STATUS 码 **Must NOT do**: - 不在 ISR 中做帧解析 — 仅在前台超级循环中处理 - 不让帧解析阻塞 SPI 接收 — 使用 Ping-Pong 缓冲区 **References**: - `src/gmsp/protocol/gmsp_crc.h` — CRC16 校验 API(Task 5 产出) - `src/gmsp/transport/spi_slave.h` — 传输层 API(Task 3 产出) - `src/drv/inc/pit32_drv.h` — 超时定时器 **Acceptance Criteria**: - [ ] 帧解析状态机正确处理所有状态转换 - [ ] 超时后正确复位状态机(不卡死) - [ ] 帧封装生成正确的 SYNC + LEN + STATUS + DATA + CRC **QA Scenarios** (Agent-Executed): ``` Scenario: 正常帧解析 Tool: UART 输出分析 Preconditions: SPI DMA 传输层就绪 Steps: 1. 主机发送: SYNC=AA55 LEN=0008 CMD=01 PAYLOAD=00000000 CRC=xxxx 2. 固件解析后 UART 打印: "frame rx: cmd=0x01 len=8 crc=OK" Expected Result: 帧解析成功,命令码匹配 Evidence: .sisyphus/evidence/task-6-frame-parse.txt Scenario: 超时复位 Tool: UART 输出分析 Steps: 1. 主机发送 SYNC+部分帧后停止 2. 等待超时 → 固件 UART 打印: "frame timeout, reset parser" 3. 主机发送新完整帧 → 固件正常解析 Expected Result: 超时后状态机复位,新帧正常接收 Evidence: .sisyphus/evidence/task-6-timeout-reset.txt ``` **Commit**: YES - Message: `feat(gmsp): binary frame protocol parser and formatter` - Files: `src/gmsp/protocol/gmsp_frame.c`, `src/gmsp/protocol/gmsp_frame.h` - Pre-commit: 编译通过 - [ ] 7. SM2 服务 + 测试向量 **What to do**: - 创建 `src/gmsp/service/sm2_svc.c/.h` - 使用 Gen2 `_2` API(`SM2GenerateKeyPair_2`, `SM2Signature_2`, `SM2Verification_2`, `SM2Encrypt_2`, `SM2Decrypt_2`, `SM2KeyExchange_2`) - 封装为统一接口:`sm2_svc_keygen(out_pub)`, `sm2_svc_sign(msg, len, ida, out_r, out_s)`, `sm2_svc_verify(msg, len, pub, r, s)`, `sm2_svc_encrypt(msg, len, pub, out_cipher)`, `sm2_svc_decrypt(cipher, len, out_plain)`, `sm2_svc_keyex(peer_pub, peer_temp_pub, out_key)` - 内部管理模块时钟(`ModuleClk_On(MODULE_CLK_TRNG_IPG_CLK/MODULE_CLK_DMA_HCLK/MODULE_CLK_CRYPTO)`) - 编码 **TDD 测试**:使用 `alg_demo.c` 中的测试向量和国密标准向量 - 测试内容:SM2 密钥生成 → 签名 → 验签(闭环验证)+ 加密 → 解密(闭环验证)+ 密钥协商(AB 双方推导一致性) **Must NOT do**: - 不要忘记初始化 TRNG(`Init_Trng()`)— 在服务初始化中调用 - 不要忘记打开时钟 — 每个操作前 `ModuleClk_On` 需要的时钟 - 不要在函数内部关闭主机可能正在使用的时钟 — 仅关闭本操作打开的 **References**: - `src/drv/inc/alg_drv.h:401-606` — SM2 Gen1 + Gen2 API 完整签名 - `src/demo/alg_demo.c` — SM2 测试向量(搜索 `SM2GenerateKeyPair_2` 等) - `AGENTS.md:anti-patterns` — SM2 时钟启用清单(A3-A8) **Acceptance Criteria**: - [ ] SM2 密钥生成 → 签名 → 验签 闭环测试 PASS - [ ] SM2 加密 → 解密 闭环测试 PASS - [ ] SM2 密钥协商 AB 双方一致测试 PASS - [ ] 所有 TDD 测试用例自检输出 "SM2: ALL TESTS PASSED" **QA Scenarios** (Agent-Executed): ``` Scenario: SM2 签名验签闭环 Tool: UART 输出分析 Preconditions: 固件编译烧录(TDD 模式) Steps: 1. 上电运行 sm2_svc 自检 2. 确认 UART 输出: "SM2 keygen...OK" → "SM2 sign...OK" → "SM2 verify...OK" 3. 确认最终输出: "SM2: 3/3 tests PASSED" Expected Result: 所有 3 个测试用例 PASS Failure Indicators: 任何 FAIL 输出 / verify 返回 0 Evidence: .sisyphus/evidence/task-7-sm2-tdd.txt Scenario: SM2 加解密闭环 Tool: UART 输出分析 Steps: 1. 上电运行 SM2 加解密自检 2. UART: "SM2 encrypt...OK" → "SM2 decrypt...OK" → "plaintext match: YES" Expected Result: 加密后解密得到原始明文 Evidence: .sisyphus/evidence/task-7-sm2-encdec.txt ``` **Commit**: YES - Message: `feat(gmsp): SM2 service with TDD test vectors` - Files: `src/gmsp/service/sm2_svc.c`, `src/gmsp/service/sm2_svc.h` - Pre-commit: TDD 测试全部 PASS - [ ] 8. SM3 服务 + 测试向量 **What to do**: - 创建 `src/gmsp/service/sm3_svc.c/.h` - 使用 Gen2 `_2` API(`SHA_Init_2(..., HASH_SM3)`, `SHA_Update_2`, `SHA_Final_2`) - 封装为统一接口:`sm3_svc_init(ctx)`, `sm3_svc_update(ctx, data, len)`, `sm3_svc_final(ctx, out_digest)`, `sm3_svc_hash(data, len, out_digest)`(一站式) - 内部管理 `MODULE_CLK_SHA` + `MODULE_CLK_DMA_HCLK` 时钟 - 编写 TDD 测试:使用 SM3 标准测试向量("abc" → 已知 SM3 哈希值) **Must NOT do**: - 不要忘记启用 SHA+EDMAC 时钟 - 不要在流式中断后不 reset SHA 状态就重新 Init **References**: - `src/drv/inc/alg_drv.h:325-368` — SHA_Init/Update/Final API(Gen1 + Gen2) - `AGENTS.md:anti-patterns` — SHA 时钟需求(A2) - 国密 SM3 标准测试向量:`"abc"` → `66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0`(32 字节) **Acceptance Criteria**: - [ ] SM3("abc") 输出匹配标准测试向量 - [ ] SM3 流式哈希(分 3 次 Update)结果与一次哈希一致 - [ ] UART 输出 "SM3: ALL TESTS PASSED" **QA Scenarios** (Agent-Executed): ``` Scenario: SM3 标准向量测试 Tool: UART 输出分析 Steps: 1. 上电运行 SM3 自检 2. UART: "SM3('abc') = 66c7f0f4..." 3. 确认输出: "SM3: 2/2 tests PASSED" Expected Result: 输出匹配标准向量 Evidence: .sisyphus/evidence/task-8-sm3-tdd.txt ``` **Commit**: YES - Message: `feat(gmsp): SM3 hash service with TDD test vectors` - Files: `src/gmsp/service/sm3_svc.c`, `src/gmsp/service/sm3_svc.h` - Pre-commit: TDD 测试 PASS - [ ] 9. SM4 ECB/CBC 服务 + 测试向量 **What to do**: - 创建 `src/gmsp/service/sm4_svc.c/.h` - 使用 Gen2 `_2` API(`SMS4_EnDecrypt_2`) - 封装为:`sm4_svc_ecb_encrypt(key, src, len, out)`, `sm4_svc_ecb_decrypt(key, src, len, out)`, `sm4_svc_cbc_encrypt(key, iv, src, len, out)`, `sm4_svc_cbc_decrypt(key, iv, src, len, out)` - 内部管理 `MODULE_CLK_SMS4` + EDMAC 时钟 - 编写 TDD 测试:SM4 标准测试向量(已知密钥 + 已知明文 → 已知密文) - 闭环测试:加密 → 解密 → 验证明文一致 **Must NOT do**: - 不要忘记数据长度必须是 16 字节整数倍(SM4 块大小) - 不要在 CBC 模式复用 IV — 每次加密需新 IV **References**: - `src/drv/inc/alg_drv.h:299-322` — `SMS4_EnDecrypt` + `SMS4_EnDecrypt_2` API - `src/demo/alg_demo.c` — SM4 测试向量(搜索 `SMS4_EnDecrypt_2`) - 国密 SM4 标准测试向量(GM/T 0002-2012 附录 A) **Acceptance Criteria**: - [ ] SM4 ECB 标准向量测试 PASS - [ ] SM4 CBC 标准向量测试 PASS - [ ] 加密→解密闭环测试 PASS - [ ] UART 输出 "SM4 ECB/CBC: ALL TESTS PASSED" **QA Scenarios** (Agent-Executed): ``` Scenario: SM4 ECB 加解密闭环 Tool: UART 输出分析 Steps: 1. 上电运行 SM4 自检 2. UART: "SM4 ECB enc...OK" → "SM4 ECB dec...OK" → "roundtrip: PASS" Expected Result: 加密后解密得到原始明文 Evidence: .sisyphus/evidence/task-9-sm4-ecb.txt ``` **Commit**: YES - Message: `feat(gmsp): SM4 ECB/CBC service with TDD test vectors` - Files: `src/gmsp/service/sm4_svc.c`, `src/gmsp/service/sm4_svc.h` - Pre-commit: TDD 测试 PASS - [ ] 10. SM4 流式加解密 + 软件 IV 包装器 **What to do**: - 在 `sm4_svc.c/.h` 中添加流式接口:`sm4_svc_stream_init(ctx, mode, key, iv)`, `sm4_svc_stream_update(ctx, data, len, out)`, `sm4_svc_stream_final(ctx)` - 实现软件 CBC IV 追踪:每次 `Update` 后用最后一个密文块更新 IV - 流式上下文结构体 `SM4_STREAM_CTX`:保存 `key[16]`, `iv[16]`, `mode`, `partial_block[16]`, `partial_len` - 处理非对齐数据:缓存不完整的块,累积到 16 字节后提交给硬件 - 编写 TDD 测试:100 字节数据分 3 次 `Update`(30+40+30)结果与一次加密一致 **Must NOT do**: - 不要假设每次 `Update` 的数据长度是 16 字节整数倍 — 必须处理非对齐 - 不要在 `Update` 之间丢失 IV 状态 **References**: - `src/drv/inc/alg_drv.h:299-322` — `SMS4_EnDecrypt_2` API - `src/gmsp/service/sm4_svc.h` — Task 9 的 SM4 ECB/CBC 接口 - `src/drv/inc/cpm_drv.h` — `MODULE_CLK_SMS4`(注意:变量名 `MODULE_CLK_SMS4 = 17`) **Acceptance Criteria**: - [ ] 100 字节数据分 3 次 Update 结果与一次加密一致 - [ ] 非 16 字节对齐数据正确处理(最后一次 Update 包含 5 字节) - [ ] UART 输出 "SM4 STREAM: ALL TESTS PASSED" **QA Scenarios** (Agent-Executed): ``` Scenario: SM4 流式分片加解密 Tool: UART 输出分析 Steps: 1. 上电运行 SM4 stream 自检 2. UART: "SM4 stream (30+40+30) bytes..." → "match: PASS" Expected Result: 分片结果 = 一次性结果 Evidence: .sisyphus/evidence/task-10-sm4-stream.txt ``` **Commit**: YES - Message: `feat(gmsp): SM4 streaming encrypt/decrypt with software IV tracker` - Files: `src/gmsp/service/sm4_svc.c`(更新), `src/gmsp/service/sm4_svc.h`(更新) - Pre-commit: 流式测试 PASS - [ ] 11. eFlash 密钥存储服务 + 安全护栏 **What to do**: - 创建 `src/gmsp/service/keystore.c/.h` - **存储区域**: `KEYS_BASE = 0x00470000`, 4 页 × 512B = 2048B - **页内布局**(Log-Structured,每页 72 字节有效载荷): ``` [magic_hi 1B=0xA5] [magic_lo 1B=0xC3] [counter 4B,大端] [key_priv 64B] [crc16 2B] 页内剩余 440B = 保留 ``` - **写入流程**: ``` 1. 扫描 4 页 → 找到 counter 最大 + CRC 有效 → 当前密钥 2. 新 counter = 当前 counter + 1 3. 目标页 = counter % 4 (轮转) 4. 若目标页有数据 → EFLASH_PageErase(addr + target_page * 512) 5. EFLASH_SetWritePermission() 6. EFLASH_WordsProg(addr + offset, data, 18 words) // 72B ÷ 4 7. EFLASH_ClrWritePermission() ``` - **寿命**: 4 页 × 10 万次/页 = 40 万次写入(SM2 密钥极少重生成,远超需求) - **上电恢复**: `keystore_init()` 扫描 4 页 → 找 counter 最大的有效页 → 加载私钥 - **安全护栏**: 所有地址操作前 `IS_KEY_ADDR(addr)` 宏校验范围;写使能窗口最短化 - 暴露 API:`keystore_init()`, `keystore_store_sm2_privkey(privkey_64bytes)`, `keystore_load_sm2_privkey(out_buf)`, `keystore_has_key()` **Must NOT do**: - 不要信任外部输入的地址 — 所有写入必须硬编码目标地址 - 不要在 eFlash 编程前忘记 `EFLASH_SetWritePermission()` + 编程后 `EFLASH_ClrWritePermission()` - ⚠️ `SetWritePermission` 是**全局**使能 — 必须在最短窗口内完成,防止野指针破坏固件 - 不要忘记 `EFLASH_Init(g_sys_clk)` 已在 `main.c` 中调用(不重复初始化) - 不要跳过 magic 校验 — 未初始化的页(0xFFFFFFFF)不能当作有效密钥 **References**: - `src/drv/inc/eflash_drv.h:10-17` — `EFLASH_PAGE_SIZE_BYTE=512`, `EFLASH_ALL_PAGE_LEN=1024`, `EFLASH_BYTE_SIZE=0x80000` - `src/drv/inc/eflash_drv.h:19-20,30,86,98,111` — `SetWritePermission()`, `ClrWritePermission()`, `EFLASH_Init()`, `EFLASH_PageErase()`, `EFLASH_WordProg()`, `EFLASH_WordsProg()` - `src/demo/eflash_demo.c` — eFlash 擦除/编程示例(`EFLASH_PageErase` + `EFLASH_WordProg` 用法) - `linkmap` — 内存布局(确认 `0x00470000-0x00470800` 不在 `.text` 段) - `src/gmsp/protocol/gmsp_crc.h` — CRC16 用于密钥页完整性校验 **Acceptance Criteria**: - [ ] 密钥存储后重新上电 → 密钥可正确读取(magic + crc 校验通过) - [ ] 地址范围外写入尝试 → 返回错误不执行 - [ ] 磨损均衡:连续 10 次写入 → 4 页全部被使用,counter 递增正确 - [ ] 未初始化页(全 0xFF)→ `keystore_has_key()` 返回 false - [ ] UART 输出 "KEYSTORE: ALL TESTS PASSED" **QA Scenarios** (Agent-Executed): ``` Scenario: 密钥持久化存储 Tool: UART 输出分析 Steps: 1. 上电存储测试密钥 → UART: "keystore write to page0(counter=1) OK" 2. 再次存储 → UART: "keystore write to page1(counter=2) OK" 3. 硬件复位 → UART: "keystore scan: found page1 counter=2 valid" 4. 验证密钥数据正确 Expected Result: 复位后从最新页正确恢复密钥 Evidence: .sisyphus/evidence/task-11-keystore-persist.txt Scenario: 磨损均衡轮转 Tool: UART 输出分析 Steps: 1. 连续写入 5 次密钥 → 观察页分配 2. UART: "page0→page1→page2→page3→erase page0→page0(counter=5)" 3. 确认所有写入后最新密钥正确 Expected Result: 4 页轮转 + 第 5 次擦除 page0 后写入 Evidence: .sisyphus/evidence/task-11-wear-level.txt ``` **Commit**: YES - Message: `feat(gmsp): eFlash log-structured key store with 4-page wear-leveling` - Files: `src/gmsp/service/keystore.c`, `src/gmsp/service/keystore.h` - Pre-commit: 持久化 + 轮转测试 PASS - [ ] 12. 命令分发器 **What to do**: - 创建 `src/gmsp/app/gmsp_dispatch.c/.h` - 实现命令码 → 处理函数的映射表(函数指针数组) - 每个命令处理函数:接收 PAYLOAD → 调用对应服务 → 构建 RESPONSE - 支持的命令码: | CMD | 操作 | 处理函数 | |-----|------|---------| | `0x01` | SM2 生成密钥对 | → `sm2_svc_keygen` → 返回公钥(64B) | | `0x02` | SM2 签名 | → `sm2_svc_sign` → 返回(R+S 64B) | | `0x03` | SM2 验签 | → `sm2_svc_verify` → 返回(1B result) | | `0x04` | SM2 加密 | → `sm2_svc_encrypt` → 返回密文(plain+96) | | `0x05` | SM2 解密 | → `sm2_svc_decrypt` → 返回明文(cipher-96) | | `0x06` | SM2 密钥协商 | → `sm2_svc_keyex` → 返回协商密钥+sA+sB | | `0x10` | SM3 哈希(一次性) | → `sm3_svc_hash` → 返回摘要(32B) | | `0x20` | SM4 ECB 加密 | → `sm4_svc_ecb_encrypt` → 返回密文 | | `0x21` | SM4 ECB 解密 | → `sm4_svc_ecb_decrypt` → 返回明文 | | `0x22` | SM4 CBC 加密 | → `sm4_svc_cbc_encrypt` → 返回密文 | | `0x23` | SM4 CBC 解密 | → `sm4_svc_cbc_decrypt` → 返回明文 | | `0x24` | SM4 流式 Init | → `sm4_svc_stream_init` → 返回 sid(1B) | | `0x25` | SM4 流式 Update | → `sm4_svc_stream_update` → 返回分片结果 | | `0x26` | SM4 流式 Final | → `sm4_svc_stream_final` → 返回最终结果+销毁会话 | | `0x30` | 导出公钥 | → 读取 eFlash → 返回公钥(64B) | | `0x31` | 生成 X.509 证书 | → `cert_svc_generate` → 返回 Base64 证书 | | `0xF0` | 获取芯片信息 | → 返回 版本(4B)+SN(8B) | - 未知命令码 → 返回 STATUS=0xFF(ERR_UNKNOWN_CMD) - **完整协议定义**: 参考 `.sisyphus/drafts/gmsp-protocol.md`(PAYLOAD 字段、STATUS 码表、数据格式规范) - 未知命令码 → 返回 STATUS=0xFF(UNKNOWN_CMD) **Must NOT do**: - 不要在分发器中做复杂业务逻辑 — 每个 handler 只做参数提取和结果封装 - 不要让 SM2 私钥通过任何命令导出(`0x30` 只导出公钥!) **References**: - `src/gmsp/protocol/gmsp_frame.h` — 命令码常量(Task 6 产出) - `src/gmsp/service/sm2_svc.h`, `sm3_svc.h`, `sm4_svc.h`, `keystore.h` — 服务层 API - `src/gmsp/protocol/gmsp_crc.h` — CRC16 帧校验 **Acceptance Criteria**: - [ ] 所有 15 个命令码正确路由到对应处理函数 - [ ] 未知命令码返回 0xFF - [ ] 每个命令的 RESPONSE 帧格式正确(STATUS + DATA + CRC) **QA Scenarios** (Agent-Executed): ``` Scenario: 命令分发集成测试 Tool: UART 输出分析 Preconditions: 所有服务层就绪 Steps: 1. 上电运行命令分发自检 2. 内部模拟发送 CMD=0x01 → 验证返回公钥(64B + STATUS=0x00) 3. 内部模拟发送 CMD=0xFF → 验证返回 STATUS=0xFF 4. UART: "DISPATCH: 15/15 commands registered, unknown cmd test PASS" Expected Result: 所有命令注册正确,未知命令被拒绝 Evidence: .sisyphus/evidence/task-12-dispatch.txt ``` **Commit**: YES - Message: `feat(gmsp): command dispatcher with 15 SM2/SM3/SM4 command handlers` - Files: `src/gmsp/app/gmsp_dispatch.c`, `src/gmsp/app/gmsp_dispatch.h` - Pre-commit: 编译通过 - [ ] 13. X.509 证书生成服务 + 测试 **What to do**: - 创建 `src/gmsp/service/cert_svc.c/.h` - 基于 SDK 现有 `src/app/cert/x509.h` + `src/app/cert/cert_sm2.c` 封装 - 实现 `cert_svc_generate(subject_name, valid_days, out_cert_base64, out_len)` - 使用芯片内 SM2 公钥 → `generate_x509_cert(CERT_PARAM*, cert_p)` - 证书参数:版本=V2, 签名算法=SM2withSM3, 主题从参数传入, 有效期从当前 RTC 时间开始 - 编写 TDD 测试:生成证书 → 用公钥验证自签名 → PASS - 证书格式:Base64 编码的 DER(PEM 风格) **Must NOT do**: - 不要忘记调用 `Init_Trng()` 和启用 SM2 相关时钟(证书生成内部使用 SM2 签名) - 不要超过 `GENERATE_CERT_MAX_LEN = 0x200`(512 字节)— 长 DN 字段会溢出 **References**: - `src/app/cert/x509.h:14-86` — `x509_struct`, `CERT_PARAM`, `generate_x509_cert()` API - `src/app/cert/cert_sm2.c` — SM2 证书生成实现 - `src/app/cert/x509.c` — X.509 解析和验证 - `include/type.h` — UINT8/UINT16/UINT32 类型 - `AGENTS.md:anti-patterns:cert` — `GENERATE_CERT_MAX_LEN` 限制 **Acceptance Criteria**: - [ ] 生成的证书 Base64 字符串可通过 `decode_base64` 解码回 DER - [ ] 证书自签名验证通过(`analyse_and_verify_x509_cert` 返回 TRUE) - [ ] UART 输出 "CERT: generate + self-verify PASS" **QA Scenarios** (Agent-Executed): ``` Scenario: X.509 证书生成与自验证 Tool: UART 输出分析 Steps: 1. 上电运行 CERT 自检 2. UART: "CERT generate...OK (234 bytes Base64)" 3. UART: "CERT self-verify...PASS" Expected Result: 证书生成成功且自签名验签通过 Evidence: .sisyphus/evidence/task-13-cert.txt ``` **Commit**: YES - Message: `feat(gmsp): X.509 self-signed certificate generation service` - Files: `src/gmsp/service/cert_svc.c`, `src/gmsp/service/cert_svc.h` - Pre-commit: 证书验证测试 PASS - [ ] 14. main.c 集成 + 主循环 **What to do**: - 更新 `src/main.c`:在 `system_dev_init()` 中添加 `gmsp_init()` - `gmsp_init()` 调用链:SPI 传输层初始化 → CRC 模块初始化 → 密钥存储初始化(从 eFlash 恢复密钥) → 服务层初始化 → 命令分发器注册 - 更新主循环 `while(1)`: ``` while(1) { if (gmsp_recv_ready()) { // DMA 完成标志 gmsp_frame_parse(&rx_buf); // 帧解析 if (frame_valid) { gmsp_dispatch(&frame); // 命令分发 gmsp_send_response(&resp); // 通过 SPI 发送应答 } } } ``` - 添加 TDD 模式:编译时 `#define GMSP_TDD_MODE` → 上电自动运行所有测试向量 → 输出结果到 UART → 停止 **Must NOT do**: - 不要在 `while(1)` 中阻塞等待 — 所有等待基于标志位 - 不要移除现有的 `reback_boot()` 和系统初始化 — 仅追加 **References**: - `src/main.c:95-173` — `app_demo()` 条件编译模式(`#ifdef GMSP_ENABLE`) - `src/gmsp/transport/spi_slave.h` — 传输层 API - `src/gmsp/protocol/gmsp_frame.h` — 帧协议 API - `src/gmsp/app/gmsp_dispatch.h` — 分发器 API - `src/common/debug.h` — `#define DEBUG_LOG` 控制 printf - `AGENTS.md:unique-styles` — demo toggle 模式 **Acceptance Criteria**: - [ ] 编译无错误(`gmsp_init()` 正确链接所有模块) - [ ] TDD 模式下上电自动运行所有测试 → UART 输出 "GMSP TDD: ALL TESTS PASSED" - [ ] 正常模式下主机发送 SPI 帧 → 芯片返回正确应答 **QA Scenarios** (Agent-Executed): ``` Scenario: TDD 模式全量回归 Tool: UART 输出分析 Preconditions: `#define GMSP_TDD_MODE` 编译 Steps: 1. 编译并烧录 2. 观察 UART 输出: "GMSP TDD START" → 各模块测试结果 → "GMSP TDD: 15/15 PASSED" Expected Result: 所有 15 个测试用例 PASS Evidence: .sisyphus/evidence/task-14-tdd-full.txt Scenario: 正常模式 SPI 握手 Tool: UART 输出分析 Steps: 1. 编译(不带 TDD_MODE)并烧录 2. 主机发送 CMD=0xF0(获取芯片信息) 3. 固件 UART: "CMD 0xF0: chip info → version=1.0, sn=xxxx" 4. SPI 应答帧包含版本号和芯片 SN Expected Result: 芯片正确响应 INFO 命令 Evidence: .sisyphus/evidence/task-14-info-cmd.txt ``` **Commit**: YES - Message: `feat(gmsp): main.c integration with TDD mode and SPI command loop` - Files: `src/main.c` - Pre-commit: TDD 全量 PASS + SPI 握手成功 - [ ] 15. 主机侧 SPI 测试工具(Linux 端,使用 spidev) **What to do**: - 创建 `tools/gmsp_test.c` — Linux 端 SPI 测试程序 - **SPI 初始化**(匹配芯片 Slave 配置): ```c #include int fd = open("/dev/spidev0.0", O_RDWR); uint8_t mode = SPI_MODE_0; // CPOL=0, CPHA=0 uint8_t bits = 8; uint32_t speed = 1000000; // 起始 1MHz ioctl(fd, SPI_IOC_WR_MODE, &mode); ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); ``` - **帧发送/接收**: ```c struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx, .len = tx_len, .cs_change = 0, }; ioctl(fd, SPI_IOC_MESSAGE(1), &tr); // 应答: rx[5]=STATUS, resp_data=&rx[6], len=(rx[2]<<8)|rx[3] ``` - 实现帧构造 + CRC16 计算(软件查表法,多项式 `0x1021`) - 测试命令:发送每个命令码 → 验证应答 STATUS=0x00 - 支持命令行参数:`--spi /dev/spidev0.0 --cmd 0x02 --data "hello" --speed 5000000` - 流式测试:100KB 随机数据 → SM4 流式 Init→Update(多次)→Final → 验证闭环 - **CS 时序**: 每帧之间拉高 CS ≥ 10μs(`.cs_change = 1` 在最后一组 transfer),确保芯片 DMA LLI 完成当前传输块 **Must NOT do**: - 不要假设 SPI 设备路径 — 使用命令行参数 - 不要在测试工具中实现业务逻辑 — 只做帧收发和结果验证 - 不要忘记 CS 间隔 — 连续帧不加间隙会导致 DMA 缓冲区溢出 - 不要跳过 CRC 校验 — 应答帧必须验证 CRC 再取 STATUS **References**: - Linux `spidev.h` — `SPI_IOC_WR_MODE`, `SPI_IOC_RD_MODE`, `SPI_IOC_MESSAGE`, `spi_ioc_transfer` - `.sisyphus/drafts/gmsp-protocol.md` — 完整协议定义(命令码、PAYLOAD 格式、STATUS 码) - `src/gmsp/protocol/gmsp_frame.h` — 帧格式常量(SYNC=0xAA55, 命令码枚举) - `src/gmsp/protocol/gmsp_crc.c` — CRC16-CCITT 算法(主机侧 C 实现复用) **Acceptance Criteria**: - [ ] 编译通过(`gcc -o gmsp_test tools/gmsp_test.c`) - [ ] `./gmsp_test --spi /dev/spidev0.0 --cmd 0xF0` → 成功获取版本+SN(13B) - [ ] `./gmsp_test --cmd 0x01` → 公钥返回 64B + STATUS=0x00 - [ ] `./gmsp_test --cmd 0x02 --data "test"` → 签名(R+S)返回 64B + STATUS=0x00 - [ ] SM4 100KB 流式闭环(Init→Update×N→Final→解密验证)→ 数据一致 **QA Scenarios** (Agent-Executed): ``` Scenario: 主机端全命令测试 Tool: Bash (编译并运行) Preconditions: CUni360S-Z 芯片上电,SPI 连接正常 Steps: 1. gcc -o gmsp_test tools/gmsp_test.c 2. ./gmsp_test --spi /dev/spidev0.0 --test-all 3. 每个命令输出: "[PASS]" 或 "[FAIL]" Expected Result: 所有 17 个命令 [PASS] Failure Indicators: 任何 [FAIL] / STATUS≠0x00 Evidence: .sisyphus/evidence/task-15-host-test.txt ``` **Commit**: YES - Message: `feat(gmsp): Linux spidev host test tool with full command coverage` - Files: `tools/gmsp_test.c` - Pre-commit: 编译通过 --- ## Final Verification Wave (MANDATORY — after ALL implementation tasks) > 4 review agents run in PARALLEL. ALL must APPROVE. - [ ] F1. **全量测试向量回归** — `unspecified-high` `#define GMSP_TDD_MODE` 编译,确认 UART 输出 "GMSP TDD: 15/15 PASSED"。 Output: `Tests [15/15 pass] | VERDICT: APPROVE/REJECT` - [ ] F2. **SPI 吞吐量测试** — `unspecified-high` 主机 `gmsp_test` 连续发送 100 个 SM4 帧(256B/帧),测量吞吐量。 Output: `Throughput [X.XX MB/s] | Frames [100/100] | Errors [0] | VERDICT` - [ ] F3. **安全审计** — `oracle` 审查:SM2 私钥导出路径、eFlash 地址护栏、密码运算时钟启用、主循环阻塞点。 Output: `Export Guard [PASS/FAIL] | Flash Guard [PASS/FAIL] | Clock Guard [PASS/FAIL] | Blocking [NONE/N found] | VERDICT` - [ ] F4. **代码质量审查** — `unspecified-high` 编译 `-Wall` 零错误零警告。无 TODO/FIXME 遗留。无硬编码路径。无 `while(1);` 死循环在非主路径。 Output: `Build [PASS/FAIL] | Warnings [N] | TODOs [N] | VERDICT` --- ## Commit Strategy | Wave | Commits | Message Pattern | |------|---------|----------------| | 0 | 2 | `chore(gmsp): verify ISR binding / RAM audit` | | 1 | 4 | `feat(gmsp): transport / IRQ3 / CRC16 / frame protocol` | | 2 | 5 | `feat(gmsp): SM2 / SM3 / SM4 / stream / keystore service` | | 3 | 4 | `feat(gmsp): dispatch / cert / integration / host tool` | --- ## Success Criteria ### Verification Commands ```bash # 编译验证 make -C Debug all # 期望: 0 errors, 0 warnings # TDD 全量回归 (芯片 UART 输出) # 期望: "GMSP TDD: 15/15 PASSED" # 主机侧集成测试 gcc -o gmsp_test tools/gmsp_test.c ./gmsp_test --spi /dev/spidev0.0 --test-all # 期望: "ALL 15 COMMANDS: PASS" ``` ### Final Checklist - [ ] 所有 15 个 TDD 测试用例 PASS - [ ] SPI 吞吐量 ≥ 1 MB/s - [ ] SM2 私钥不出芯片(安全审计确认无导出路径) - [ ] eFlash 密钥存储地址范围校验有效 - [ ] 主机侧 `gmsp_test` 全命令通过 - [ ] 编译零警告零错误