K230 Secure Boot 使用说明#
本文档说明如何在 K230 RTOS SDK 中配置、构建、烧录并验证 Secure Boot。
Secure Boot 设计简介#
K230 RTOS SDK 的 Secure Boot 设计可以概括为一句话:由硬件先建立 spl 的信任,再由 spl 继续建立下游 firmware 的信任,整个软件栈只维护两级安全配置。
设计上有四个关键点:
信任链分两段:BROM 负责校验或解密
spl,U-Boot SPL 负责校验或解密下游firmware。配置模型只保留
spl和firmware两个阶段:u-boot.bin、opensbi_rtt_system.bin、rtapp统一归到firmware,共享同一套密钥和 OTP 槽位策略;其中下游firmware的AES-GCM IV和SM4-CBC IV都按镜像动态生成。密钥材料不直接散落在镜像里:对称密钥和公钥哈希写入 OTP,对应锁策略单独生成,运行时通过
OTPKEY_x槽位让硬件安全单元参与解密和验签。构建产物分层清晰:镜像打包负责生成带安全头的
spl和下游镜像,OTP 工具负责生成otp_config.json、otp_data.kdimg、otp_key_lock.kdimg、otp_full.kdimg,方便先检查再烧录。
下面这张图说明 Secure Boot 的配置入口、产物关系和信任链:
只包含 spl / firmware") --> B("镜像打包脚本
生成安全镜像"); A --> C("gen_otp_config.py
生成 OTP 配置和 kdimg"); A --> D("gen_uboot_secure_header.py
生成辅助头文件"); B --> E("安全 SPL 镜像
u-boot-spl.bin"); B --> F("安全 firmware 镜像
u-boot.bin / opensbi_rtt_system.bin / rtapp.elf.gz"); C --> G("otp_config.json"); C --> H("otp_data.kdimg / otp_key_lock.kdimg / otp_full.kdimg"); H --> I("OTP 中的密钥、公钥哈希和锁位"); I --> J("BROM"); J --> E; E --> K("U-Boot SPL 运行时"); D --> K; I --> K; K --> F;
理解这四点后,后面的配置、构建和烧录步骤就可以直接按“先确定阶段配置,再生成 OTP,最后烧录并验证启动”的思路来执行。
先看整体流程#
如果只关心怎么落地,可以直接按下面流程操作:
选择一个带 Secure Boot 的 defconfig,例如执行
make k230_rtos_evb_secureboot_defconfig。不要直接复用仓库里的 demo 配置,先执行
python tools/gen_secureboot_configs.py --output-dir <你的 secureboot 输出目录>生成新的板级 Secure Boot 配置和密钥材料。执行
make menuconfig,打开spl和firmware的 Secure Boot 选项,并关闭Prebuilt Uboot。先执行
make uboot,生成源码版 U-Boot、辅助头文件以及 OTP 输出文件。打开
otp_config.json,确认 OTP 槽位、锁策略和生成的otp_data.kdimg、otp_key_lock.kdimg、otp_full.kdimg都符合预期。再执行
make -j9,生成完整镜像,例如fn_u-boot-spl.bin、fn_ug_u-boot.bin、opensbi_rtt_system.bin、rtapp.elf.gz。先烧录 OTP,再烧录镜像,上电检查串口日志,确认能够正常解密并启动。
如果你是第一次接入 Secure Boot,推荐按下面顺序做:
先验证普通非 Secure Boot 镜像能正常启动。
再打开 Secure Boot 开关并生成
otp_config.json,只检查配置和输出文件,不急着烧 OTP。确认
otp_config.json中的 key/hash 槽位和锁策略正确后,再烧录 OTP。最后烧录加密镜像并做串口启动验证。
Secure Boot 模型#
模式#
模式值 |
算法组合 |
说明 |
|---|---|---|
|
无加密 + Hash |
仅完整性校验 |
|
SM4 + SM2 |
国密方案 |
|
AES + RSA |
国际方案 |
只有两个配置阶段#
配置阶段 |
典型镜像 |
谁负责解密/校验 |
|---|---|---|
|
|
BROM |
|
|
U-Boot SPL |
这里最关键的一点是:
JSON 顶层只允许
spl和firmware两个对象。u-boot.bin、opensbi_rtt_system.bin、rtapp.elf.gz共用同一套firmware密钥和 OTP 槽位策略;其中AES-GCM IV和SM4-CBC IV都按镜像动态生成。
使用前必须知道的规则#
spl 的 IV 不能自定义#
spl 阶段必须使用 BROM 固定 IV。
如果 JSON 中没有填写
spl的 IV,打包脚本会自动补成固定值。如果 JSON 中填写了不同的 IV,脚本会覆盖为固定值,并输出 warning。
因此,spl 的 IV 以硬件规则为准,不以 JSON 中的填写值为准。
firmware 的 AES IV 按镜像动态生成,不写入 OTP#
下游 firmware 的 AES + RSA 流程如下:
每个镜像在打包时动态生成新的
AES-GCM IV。该 IV 会直接写入镜像密文载荷头部。
U-Boot SPL 运行时从镜像中读取这个 IV,再结合 OTP 中的对称密钥完成 GCM 解密。
因此:
firmware的AES-GCM IV不需要写入 OTP。firmware的AES-GCM IV不通过头文件固化进 U-Boot。如果你看到
otp_config.json中没有 IV 字段,这是符合当前设计的。
firmware 的 SM4 IV 也按镜像动态生成#
下游 firmware 的 SM4 + SM2 流程如下:
每个镜像在打包时动态生成新的
SM4-CBC IV。该 IV 会直接写入镜像密文载荷头部。
U-Boot SPL 运行时从镜像中读取这个 IV,再结合 OTP 中的对称密钥完成
SM4解密。
因此:
firmware的SM4 IV不需要写入 OTP。firmware的SM4 IV不通过头文件固化进 U-Boot。如果你看到
otp_config.json中没有SM4 IV字段,这是符合当前设计的。
firmware 共用一组 OTP 槽位#
OTP 槽位策略如下:
阶段 |
模式 |
对称密钥槽位 |
公钥哈希槽位 |
|---|---|---|---|
|
SM4 + SM2 |
|
|
|
AES + RSA |
|
|
|
SM4 + SM2 |
|
|
|
AES + RSA |
|
|
这组下游 firmware 槽位需要和 U-Boot 运行时解密代码保持一致。源码中的运行时定义如下:
AES + RSA使用OTPKEY_3和OTPKEY_8SM4 + SM2使用OTPKEY_5和OTPKEY_9
OTP 里只放对称密钥和公钥哈希,不放 IV 或签名随机数#
tools/gen_otp_config.py 会为每个阶段生成两类 OTP 条目:
对称密钥
公钥哈希
以下内容不会写入 OTP:
AES-GCM IVSM4 IVSM2 random_k私钥本身
以下内容不会写入 OTP:
AES-GCM IV:spl使用 BROM 固定 IV,firmware使用镜像中携带的动态 IV。SM4 IV:spl使用固定 BROM IV,firmware使用镜像中携带的动态 IV。SM2 random_k:仅在签名时作为一次性随机数使用,运行期验签不需要,也不应固化到 OTP。
对称密钥和公钥哈希的默认锁策略#
tools/gen_otp_config.py 的锁策略是:
对称密钥条目使用
NA锁策略。公钥哈希条目使用
RO锁策略。
每个 OTP 槽位的锁位都是按 32 字节整槽写入,不会只锁实际数据长度对应的前半段字节。也就是说,哪怕 SM4 对称密钥本身只有 16 字节,最终也会锁住完整的 32 字节槽位。
因此在 otp_key_lock.kdimg 中,你会看到:
对称密钥槽位写入
NA公钥哈希槽位写入
RO
实际烧录地址由 kdimg 分区项中的 offset 控制。
这不是异常行为。U-Boot 在运行时并不会去直接读取明文密钥,而是通过 PUFS API 按 OTPKEY_x 槽位调用硬件加解密单元。密钥可以被锁为 NA,但硬件仍然可以按槽位使用它。
RT-Smart 运行期代码执行约束#
Secure Boot 只信任启动链中已经被 firmware 规则保护的内容,不信任运行期从文件系统重新读入的原始可执行文件。
因此在 CONFIG_SECURE_BOOT_FIRMWARE_ENABLE=y 时,需要按下面的规则理解 RT-Smart 的执行模型:
允许执行的 RT-App 入口是
@preload,它是一个内部 auto-exec 触发符,不对应文件系统中的真实 ELF,也不能从msh里当作普通命令手工执行;运行时会改为读取启动阶段已经预加载到内存中的rtapp。不允许从文件系统直接加载裸 ELF。
不允许在运行期再从文件系统加载
/lib/ld.so。不允许启用
dlmodule、dlopen、.mo模块执行这类动态模块机制。
另外,CONFIG_RTT_AUTO_EXEC_CMD 在 Secure Boot 场景下也有明确约束:
该配置必须以
@preload开头。如果配置成其他命令,系统会在启动时打印提示并跳过 auto exec,而不会回退去执行文件系统中的应用。
如何配置#
Kconfig 选项#
Secure Boot 相关配置项包括一组 spl 和一组 firmware:
CONFIG_SECURE_BOOT_SPL_ENABLECONFIG_SECURE_BOOT_SPL_SM4_SM2/CONFIG_SECURE_BOOT_SPL_AES_RSACONFIG_SECURE_BOOT_FIRMWARE_ENABLECONFIG_SECURE_BOOT_FIRMWARE_SM4_SM2/CONFIG_SECURE_BOOT_FIRMWARE_AES_RSACONFIG_SECURE_BOOT_CONFIG_FILECONFIG_RTT_AUTO_EXEC_CMD
其中 CONFIG_RTT_AUTO_EXEC_CMD 需要特别注意:
非 Secure Boot 场景下,它仍然可以配置成普通 shell 自动执行命令。
Secure Boot 场景下,它必须配置为以
@preload开头的字符串,推荐保持默认值@preload &。这里的
@preload只用于内部 auto-exec 入口,不是提供给msh的通用命令接口。
如果要在运行期直接查询或演练 OTP 安全配置,还需要关注下面三个 Kconfig:
RT_PUFS_ENABLE_BUILTIN_CMD:打开内核侧pufs_otp/pufs_otp_sec命令。RT_USING_PUFS_FILE_HASH:打开sha256和兼容入口pufs_file_hash。RT_PUFS_OTP_WRITE_ENABLE:控制 OTP 写入、锁定、key-to-OTP、zeroize 是否真的提交到硬件;关闭时只做 dry-run。
仓库中提供了一个可直接参考的示例 defconfig:
make k230_rtos_evb_secureboot_defconfig
该示例默认使用 SM4 + SM2。如果你要使用 AES + RSA,可以在 menuconfig 中切换算法后保存。
一个 AES + RSA 的 .config 片段示例如下:
CONFIG_SECURE_BOOT_CONFIG_FILE="secureboot/secure_config_aes_rsa_pem.json"
CONFIG_SECURE_BOOT_SPL_ENABLE=y
CONFIG_SECURE_BOOT_SPL_AES_RSA=y
CONFIG_SECURE_BOOT_FIRMWARE_ENABLE=y
CONFIG_SECURE_BOOT_FIRMWARE_AES_RSA=y
建议将 CONFIG_SECURE_BOOT_CONFIG_FILE 配置为你自己生成的板级相对路径,例如:
CONFIG_SECURE_BOOT_CONFIG_FILE="secureboot_local/secure_config_sm4_sm2.json"
JSON 配置文件结构#
不要直接拿仓库里的 demo 配置量产。
推荐先生成一份新的 Secure Boot 配置和密钥材料,例如:
python tools/gen_secureboot_configs.py --output-dir boards/k230_evb/secureboot_local
生成后再把 CONFIG_SECURE_BOOT_CONFIG_FILE 指向该目录中的 JSON。
默认情况下,生成器会同时写出:
secure_config_aes_rsa_pem.jsonsecure_config_sm4_sm2.jsonspl_rsa_pub.pem/spl_rsa_priv.pemfirmware_rsa_pub.pem/firmware_rsa_priv.pem
推荐将 Secure Boot 配置和密钥材料统一放在板级目录下,例如:
boards/k230_evb/secureboot_local/
JSON 顶层结构如下:
{
"spl": {
"firmware": {
"version_bytes": "00000000"
},
"aes": {
"key": "24501ad384e473963d476edcfe08205237acfd49b5b8f33857f8114e863fec7f",
"auth_data": ""
},
"rsa": {
"key_size": 2048,
"public_key_file": "spl_rsa_pub.pem",
"private_key_file": "spl_rsa_priv.pem"
}
},
"firmware": {
"firmware": {
"version_bytes": "00000000"
},
"aes": {
"key": "24501ad384e473963d476edcfe08205237acfd49b5b8f33857f8114e863fec7f",
"auth_data": ""
},
"rsa": {
"key_size": 2048,
"public_key_file": "firmware_rsa_pub.pem",
"private_key_file": "firmware_rsa_priv.pem"
}
}
}
请注意:
firmware段是所有下游镜像共享的唯一安全配置。对于
AES + RSA:通常不需要在 JSON 中填写aes.iv;spl会使用 BROM 固定 IV,firmware则按镜像动态生成 IV。对于
SM4 + SM2:spl.sm4.iv使用 BROM 固定 IV,生成脚本会写入这个固定值;firmware按镜像动态生成 IV,JSON 中通常不包含firmware.sm4.iv。
字段说明#
通用字段#
firmware.version_bytes:4 字节版本号。
AES + RSA 模式#
aes.iv:可选。spl阶段使用固定 BROM IV;firmware阶段默认每个镜像自动生成新的AES-GCM IV并写入镜像,因此通常不需要手工配置。aes.key:32 字节 AES 密钥。aes.auth_data:可选附加认证数据。rsa.key_size:使用 2048。rsa.public_key_file/rsa.private_key_file:可直接引用 PEM 文件。也支持直接写
rsa.modulus、rsa.exponent、rsa.private_exponent。
SM4 + SM2 模式#
sm4.key:16 字节密钥。sm4.iv:spl阶段使用固定 BROM IV;firmware阶段每个镜像都会自动生成新的SM4-CBC IV并写入镜像,因此通常不需要配置firmware.sm4.iv。sm2.private_key、sm2.public_key_x、sm2.public_key_y、sm2.id:SM2 必填材料。sm2.random_k:不再要求用户配置。当前签名流程会为每次签名自动生成新的随机k,即使 JSON 中保留该字段也会被忽略。
构建步骤#
激活环境并选择配置#
source ~/.canmv_venv/bin/activate # 可选
make k230_rtos_evb_secureboot_defconfig
make menuconfig
如果不使用 k230_rtos_evb_secureboot_defconfig,也可以先选择自己的板级 defconfig,再在 menuconfig 中手动打开 Secure Boot。
先编译源码版 U-Boot#
make uboot
这个步骤除了编译 U-Boot 外,还会生成两类关键文件:
辅助头文件:
src/uboot/uboot/board/kendryte/common/secure_boot_config_autogen.hOTP 输出文件:
output/<defconfig>/images/uboot/otp_config.json、otp_data.kdimg、otp_key_lock.kdimg、otp_full.kdimg
如果你修改了密钥配置或者 Secure Boot 模式,这一步必须重跑。firmware 镜像使用的动态 IV 由镜像打包阶段写入载荷,不依赖头文件中的固定值。
再构建完整镜像#
make -j9
顶层构建会继续打包:
fn_u-boot-spl.binfn_ug_u-boot.binopensbi_rtt_system.binrtapp.elf.gz
其中 u-boot.bin、opensbi_rtt_system.bin、rtapp.elf.gz 都会使用同一套 firmware Secure Boot 配置。
构建完成后重点检查哪些文件#
以 k230_rtos_evb_secureboot_defconfig 为例,建议重点检查下面这些输出物:
文件 |
用途 |
|---|---|
|
打包后的 SPL 镜像 |
|
打包后的 U-Boot 主镜像 |
|
打包后的 OpenSBI + RT-Smart 镜像 |
|
打包后的 RT-App 镜像 |
|
OTP 槽位和写入值说明 |
|
仅包含 OTP 数据区的 kdimg |
|
仅包含 OTP 锁位区的 kdimg |
|
同时包含 OTP 数据区和锁位区的 kdimg |
OTP 文件怎么理解#
otp_config.json#
这个文件是最重要的检查入口。烧录前请先确认:
slot_policy是否符合预期。stages中是否只出现了你启用的阶段。spl和firmware的密钥、公钥哈希是否写入了正确槽位。不要期待在这个文件里看到
IV或SM2 random_k,这些信息不会写入 OTP。
对于 AES + RSA:
spl写入OTPKEY_2和OTPKEY_6firmware写入OTPKEY_3和OTPKEY_8
对于 SM4 + SM2:
spl写入OTPKEY_4和OTPKEY_7firmware写入OTPKEY_5和OTPKEY_9
otp_data.kdimg#
这是一个 kdimg 容器,内部只有一个分区项:
分区名为
otp_data烧录目标 offset 为
0内容只覆盖 OTP 数据区,不包含锁位区
otp_key_lock.kdimg#
这也是一个 kdimg 容器,内部只有一个分区项:
分区名为
otp_key_lock烧录目标 offset 为
1024内容只覆盖 OTP 锁位区
默认情况下:
spl对称密钥槽位会被锁。firmware对称密钥槽位也会被锁。spl和firmware的公钥哈希槽位会被锁为RO。
如果同时启用了 spl 和 firmware,理论上应该能在对应槽位位置看到两组锁位,而不只是一组。
otp_full.kdimg#
这是一个包含两个分区项的 kdimg:
otp_data,target offset 为0otp_key_lock,target offset 为1024
适合一次性烧录完整 OTP 内容。
运行期 OTP 安全配置接口#
除了离线生成 otp_config.json 和 kdimg 之外,SDK 还提供了运行期的 OTP 安全配置接口,主要用于查询状态、设置少量安全位以及在确认后锁定配置字。
建议在量产时必须配置以下选项,除
disable_isp可视使用需求来决定。
支持的安全位包括:
disable_spi2axidisable_jtagforce_secure_bootdisable_isp
这四个状态分别位于三个 RT OTP 配置字中:
0x0000:disable_spi2axi0x0004:disable_jtag0x000C:force_secure_boot和disable_isp
其中 disable_spi2axi 的语义是:
0:允许 SPI2AXI,默认值1:禁止 SPI2AXI
这些位遵循 OTP 的一次性编程语义:
只能从
0 -> 1不能从
1 -> 0锁定后对应配置字只能继续读,不能再写
用户态 HAL 对应接口为:
drv_pufs_otp_apply_security_config()drv_pufs_otp_get_security_config_state()drv_pufs_otp_lock_security_config_words()
其中 drv_pufs_otp_get_security_config_state() 返回的状态结构除了逻辑位本身,还会返回:
spi2axi_word_lockjtag_word_lockboot_ctrl_word_lock
内核态命令入口为:
pufs_otp read <addr> <len>:读取扁平 OTP 地址空间中的原始内容pufs_otp_sec query:查询当前 SPI2AXI / JTAG / Secure Boot / ISP 状态以及对应 lock 状态pufs_otp_sec write <spi2axi|jtag|secure_boot|isp|all> [...]:把指定安全位写成1pufs_otp_sec lock:把0x0000、0x0004和0x000C三个配置字锁成RO
一个典型的运行期调用流程如下:
pufs_otp_security_state_t state;
drv_pufs_otp_get_security_config_state(&dev, &state);
drv_pufs_otp_apply_security_config(&dev,
true, /* disable_spi2axi */
false, /* disable_jtag */
false, /* force_secure_boot */
false); /* disable_isp */
drv_pufs_otp_lock_security_config_words(&dev);
对应的板上命令可以写成:
pufs_otp_sec query
pufs_otp_sec write spi2axi
如果启用了文件摘要命令,还可以使用:
sha256 <path>:固定计算文件的 SHA-256pufs_file_hash <path> [sha224|sha256|sha384|sha512|sha512_224|sha512_256|sm3]:兼容入口,支持手工指定摘要算法
这组命令主要用于板上验证,不替代量产阶段的 OTP 生成和烧录流程。
Dry-run 行为怎么理解#
RT_PUFS_OTP_WRITE_ENABLE 默认关闭。在默认配置下,OTP 相关操作不会真的写硬件,而是进入 dry-run 模式。
dry-run 的行为包括:
会先检查 lock 状态、地址范围和 OTP 位方向是否合法
如果请求非法,例如试图做
1 -> 0,会直接拒绝如果请求合法,则打印“准备写什么、写到哪里”,但不会真正提交到 OTP
RT OTP 和 CDE OTP 的 dry-run 日志都已经细化到逐 word 级别,典型日志格式如下:
OTP WRITE DRY-RUN WORD: addr=0x004 cur=0x00000000 new=0x00000001CDE WRITE DRY-RUN WORD: offset=0x000 cur=0x00000000 new=0x00000001
因此在正式打开 RT_PUFS_OTP_WRITE_ENABLE 前,建议先通过 dry-run 检查:
目标地址或 offset 是否正确
cur和new的差异是否符合预期是否只发生了允许的
0 -> 1置位
烧录和验证建议#
烧录 OTP 前先做三件事#
确认板级电压、启动介质和串口配置正确。
打开
otp_config.json,逐项确认槽位、算法和写入值。确认当前镜像已经能正常构建,并且明文启动流程没有问题。
烧录 OTP#
OTP 烧录操作请配合 how_to_change_otp.md 一起使用。
如果当前只是想先确认运行期 API 或命令的行为,建议先保持 RT_PUFS_OTP_WRITE_ENABLE=n,使用 dry-run 观察日志,不要直接在开发板上提交 OTP 写入。
实际烧录时,建议按以下顺序:
先根据需要选择
otp_data.kdimg+otp_key_lock.kdimg,或者直接选择otp_full.kdimg。确认
otp_config.json中的 key/hash 槽位已经和当前配置匹配,例如firmware的AES + RSA为OTPKEY_3/8,SM4 + SM2为OTPKEY_5/9。再烧录加密后的镜像。
最后上电检查串口日志。
启动验证#
正常情况下应能看到:
spl正常进入 U-Boot。u-boot、opensbi_rtt_system.bin或rtapp.elf.gz能被正常解密加载。OpenSBI、RT-Smart 和应用流程继续启动。
常见失败现象#
日志或报错 |
常见原因 |
|---|---|
|
JSON 中缺少对应算法必填字段,常见于 |
|
OTP 中的 RSA 公钥哈希与镜像签名使用的公钥不一致 |
|
RSA 签名材料不一致或镜像被破坏 |
|
|
|
OTP 中的 SM2 公钥哈希与镜像使用的公钥不一致 |
|
SM2 签名材料不一致或镜像被破坏 |
|
|
|
镜像版本号小于 OTP 中记录的版本 |
|
解密后的明文不是合法镜像,通常是密钥、IV 或算法不匹配 |
|
同一 OTP 槽位被写入了不同内容 |
|
试图把已经置位的 OTP bit 清回 |
|
目标配置字或槽位已经被锁,运行期写入被拒绝 |
使用建议#
调试阶段不要依赖预编译 U-Boot#
调试 Secure Boot 时,建议关闭预编译模式,优先使用源码版 U-Boot。这样在你修改密钥配置或算法后,辅助头文件和 U-Boot 本体能够同步更新。
先验证普通启动,再烧录 OTP#
推荐顺序是:
先验证普通镜像能正常启动。
再验证 Secure Boot 镜像能正常构建。
最后再烧录 OTP。
这样可以把镜像问题和 OTP 问题分开排查。
修改安全材料后先重编 U-Boot#
只要修改了下游 firmware 的密钥或算法,就应该先重新执行:
make uboot
否则辅助头文件和相关打包产物仍可能保留旧配置。
先用运行期查询命令核对板上状态#
如果板子已经写入过部分 OTP,建议在继续操作前先通过命令读取当前状态:
pufs_otp_sec query
pufs_otp read 0x0004 4
pufs_otp read 0x000C 4
这样可以先确认:
JTAG / Secure Boot / ISP 位是否已经被编程
对应配置字是否已经被锁定
板上状态是否和
otp_config.json、量产预期一致
在开发阶段把文件系统执行面收紧理解清楚#
Secure Boot 打开后,启动链信任的是 spl -> firmware,不信任运行期从文件系统重新取回的原始 ELF。
因此开发阶段不要把下面这些行为当成“Secure Boot 仍然有效”的前提:
从文件系统手工加载裸 ELF
运行期再加载
/lib/ld.so打开
dlmodule、dlopen或类似动态模块装载能力
如果确实需要做后装应用分发,应该设计新的受保护应用格式,并复用现有 firmware 级别的验签/解密链路。
相关文件#
如需继续查看实现细节,可参考:
顶层配置:
.configSecure Boot Kconfig:
Kconfig.secureboot示例 defconfig:
configs/k230_rtos_evb_secureboot_defconfig板级示例配置:
boards/k230_evb/secureboot/U-Boot 打包脚本:
tools/gen_image_uboot.pyOpenSBI + RT-Smart 打包脚本:
tools/gen_image_opensbi.pyRT-App 打包脚本:
tools/gen_image_rtapp.pyOTP 生成脚本:
tools/gen_otp_config.pyU-Boot 辅助头文件生成脚本:
tools/gen_uboot_secure_header.pySecure Boot 配置解析:
tools/image_tools/k230_image_generator.py
