TXW81x:XIP 运行时 Flash 加密写(SET_ENCRYPT)不可用 / 极慢
一、环境
| 项 | 值 |
|---|---|
| 芯片 | TXW817-824 |
| SDK | hgSDK v2.5.4.3-41396(libflash / libcore 同版本) |
| 运行方式 | 程序在 SPI Flash XIP 上执行(SPI_NOR_XIP_MODE,QSPI/spi7) |
| OS | RTOS(有任务 / mutex;spi_nor_write 内 os_mutex_lock) |
| 镜像布局 | ildr @ 0x10000,iapp @ 0x90000,size 0x100000 |
| makecode | CodeAddrOffset=0x1000,AesEnable=0 |
| 分区表 | ildr / iapp 等带 encrypted 标志 |
| 工厂烧录 | AesEnable=0 + PT encrypted → 上电 txloader:ENCRYPT×N → VERIFY → RUN 正常 |
加密 remap 约定(与官方注释一致):
remap_base = partition_flash_addr + CodeAddrOffset
= 0x90000 + 0x1000 = 0x91000 // iapp 为例
HDR [0, 0x1000) 明文;body [0x1000, ...) 需硬件加密写入
二、问题描述(摘要)
需求:Loader(XIP)做 OTA 时,按官方方式把 明文 body 写成与 txloader ENCRYPT 后相同的 片上 Flash 密文,以便复位后 VERIFY / 加密 XIP 启动。
现象:
- 工厂 / txloader
ENCRYPT:正常(encrypt bin done→verify bin done→ RUN)。 - 应用内官方加密写(见下文伪代码):
- 若按示例
__disable_irq()包住spi_nor_write:触发
** FATAL CALL , IRQ DISABLED **(pend_to_blk_obj,mutex/信号量在关中断下阻塞)。 - 若 去掉关中断(仅保留
SET_ENCRYPT+REMAP+spi_nor_write):能写完,但极慢,实测约24000 ms / 1KB(约 24s/KB,整包 ~750KB body 不可接受)。
- 若按示例
- OTA 只写明文 body(不开
SET_ENCRYPT):Loader 内 CRC 可对;复位后 txloader 不再 ENCRYPT,直接 VERIFY →boot_data_crc compare fail(PT 仍 encrypted)。 - HDR
aes_en=0(与 makecode AesEnable=0 一致):不能让 VERIFY 按明文介质处理;加密策略以 PT encrypted 为准。 - 公开 SDK 无「用 Flash 片内密钥在 RAM 加密 buffer,再明文
spi_nor_write」的 API(仅有通用sysaes,需自备 key;efuse只能取 key 的 CRC)。
诉求:请官方给出 XIP + RTOS 下 对 encrypted 分区做 OTA 加密写的推荐做法(或确认不支持),例如:
- 是否必须把写路径放到 SRAM 再
SET_ENCRYPT? - 是否有 host 预加密 工具/算法与片内一致?
- 是否有接口 强制再次 ENCRYPT(OTA 明文写后)?
SET_ENCRYPT期间与 XIP 取指的官方约束 / 时序(WIP、wip.tms)?
三、官方路径伪代码(问题复现用)
与 SDK/spi.h 注释及官方 sample 中 hal_partition_write_encrypt 一致:
/* __CODE_OFFSET == makecode CodeAddrOffset == 0x1000 */
void official_flash_encrypt_program(uint32_t part_addr,
uint32_t dst_offset, /* >= 0x1000 for body */
const uint8_t *plain, uint32_t len)
{
struct spi_nor_flash *flash = (struct spi_nor_flash *)dev_get(HG_FLASH0_DEVID);
uint32_t remap = part_addr + __CODE_OFFSET; /* e.g. 0x91000 */
uint32_t irq;
spi_nor_open(flash);
/* --- 官方示例常见写法:关中断 --- */
irq = __disable_irq();
spi_nor_ioctl(flash, SPI_XIP_CUSTOM_SET_ENCRYPT, 1, 0);
spi_nor_ioctl(flash, SPI_XIP_CUSTOM_SET_REMAP, remap, 0);
/* 写 Flash 绝对地址,不是 remap 后的 CPU 地址 */
spi_nor_write(flash, part_addr + dst_offset, (uint8_t *)plain, len);
spi_nor_ioctl(flash, SPI_XIP_CUSTOM_SET_ENCRYPT, 0, 0);
spi_nor_ioctl(flash, SPI_XIP_CUSTOM_SET_REMAP, 0, 0);
enable_irq(irq);
spi_nor_close(flash);
}
对应 SDK 枚举(sdk/include/hal/spi.h):
SPI_XIP_CUSTOM_SET_REMAP, // 配置 remap;需 SET_ENCRYPT=1 才生效
SPI_XIP_CUSTOM_SET_ENCRYPT, // 是否强行加密
变体 A(与示例一致,关中断) → 本板 RTOS:FATAL CALL, IRQ DISABLED。
变体 B(去掉 disable_irq/enable_irq,其余相同) → 本板:功能上可写,~24s/KB。
明文对照(同 XIP):
/* HDR 或实验明文 body:不开 ENCRYPT */
spi_nor_open(flash);
spi_nor_write(flash, part_addr + dst_offset, plain, len); /* 正常快 */
spi_nor_close(flash);
擦除(64KB block)1MB 约百毫秒级,说明普通擦写正常。
四、复现步骤(简)
- 工厂烧录:PT
iappencrypted,AesEnable=0,确认上电有ENCRYPT/VERIFY/RUN。 - 从 XIP Loader 擦除
iapp,写 HDR[0,0x1000)明文(快)。 - 对 body
offset>=0x1000调用上文official_flash_encrypt_program:- 带
__disable_irq→ 观察 FATAL; - 或不关中断 → 打点测量
spi_nor_write耗时(本板 ~24000ms/1024B)。
- 带
- (可选)body 改明文写后
mcu_reset:观察多为INIT→INFO→VERIFY(无 ENCRYPT),encrypted 槽 CRC fail。
五、已排除 / 已观察
| 项 | 结论 |
|---|---|
ioctl 顺序 / remap=part+0x1000 / 绝对地址写 |
与官方一致 |
| 仅写 4KB HDR | 不会进加密写(预期) |
| 去 BLE、关 PSRAM_HEAP | 未把 24s/KB 降到可用 |
spi7.wip.tms=60000(无 erase-suspend 的 FAQ 配法) |
erase 仍快;不解释为「每次等满 60s」,但是否加重 ENCRYPT+XIP 待官方确认 |
HDR aes_en=0 |
不能改为明文 VERIFY |
| 清 PT encrypted | 双槽 VERIFY 失败 → END(已恢复) |
六、希望官方明确回答
- XIP 执行代码时,是否支持对同 Flash 做
SET_ENCRYPT+spi_nor_write?官方推荐流程是什么? - 示例中的 **
__disable_irq+ spi_nor_write** 在带 mutex 的libflash` 上如何避免 FATAL?是否必须 SRAM 重定位写函数? - 有无 与片内 Flash 密钥一致 的预加密接口/工具(PC 或片上 buffer 加密)?
- OTA 明文写入后,如何 触发与工厂相同的 ENCRYPT?
24000ms/1KB是否为已知限制?可接受优化手段?
七、日志摘录
关中断 FATAL:
enc-wr enter ... (or WR into encrypt body @0x91000)
** FATAL CALL , IRQ DISABLED **
assertation ... pend_to_blk_obj ... Task:iplld_ws
不关中断极慢:
enc-wr off=0x1000 size=0x400 24000ms
工厂路径正常:
cur_st: ENCRYPT
working at 0, encrypt bin done
working at 1, encrypt bin done
cur_st: VERIFY
working at 0, verify bin done
cur_st: RUN
明文 OTA 后复位(无再 ENCRYPT):
cur_st: INIT → INFO → VERIFY // 无 ENCRYPT
... boot_data_crc compare fail ...