1. STM32H563启动模式与Boot Mode机制
STM32H563的启动模式(Boot Mode)是理解整个固件升级方案的逻辑起点。与传统STM32F系列仅依赖BOOT0引脚决定启动路径不同,STM32H5系列引入了Product State这一选项字节维度,使启动路径的决策更加灵活,也为硬件受限场景提供了软件替代方案。
根据RM0481参考手册,当TrustZone未使能(TZEN=0xC3)时,启动模式由PRODUCT_STATE选项字节、BOOT0引脚电平以及用户选项字节中的NSBOOTADD0/NSBOOTADD1共同决定。具体规则如下表所示:
| PRODUCT_STATE | BOOT0引脚 | 启动地址定义(选项字节) | 启动区域 | ST编程默认值 |
| Open | 0 | NSBOOTADD0[31:8] | 用户Flash(由选项字节定义) | Flash: 0x08000000 |
| Open | 1 | NSBOOTADD1[31:8] | 系统Bootloader | Bootloader |
| - | Provisioning | NA | RSS | Bootloader |
| Provisioned, Closed, Locked | x | NSBOOTADD0[31:8] | 用户Flash(由选项字节定义) | Flash: 0x08000000 |
从上表可以看出,当PRODUCT_STATE为Open时,BOOT0引脚的电平直接决定启动区域:BOOT0为0时从用户Flash启动,BOOT0为1时进入系统Bootloader。而当PRODUCT_STATE处于Provisioning状态时,无论BOOT0电平如何,芯片均进入RSS(Root Security Service,根安全服务)区域,用于执行安全配置操作。当PRODUCT_STATE处于Provisioned、Closed或Locked状态时,启动路径完全由选项字节NSBOOTADD0决定,BOOT0引脚被忽略。
这一机制正是本方案的技术基石:在BOOT0被硬件固定为高电平的情况下,通过将Product State从Open切换至Closed状态,即可使芯片忽略BOOT0引脚,转而从选项字节指定的用户Flash地址启动。上电复位后,芯片首先读取Product State选项字节,根据其值执行相应的启动决策流程,这一过程完全由硬件自动完成,无需用户程序干预。
资料获取:实战经验 | LAT1726 如何通过STM32H5 Bootloader进行固件升级与运行
2. Product State状态机深度解析
Product State是STM32H5系列引入的核心安全状态管理机制,通过选项字节中的PRODUCT_STATE字段(代码为OB)实现。该机制定义了8种产品状态,每种状态对应不同的调试策略与安全策略,形成一个从完全开放到完全锁定的渐进式安全模型。
| PRODUCT_STATE(代码) | 状态名称 | 调试策略 | 安全策略描述 |
| 0x2ED | Open | 开放(TZECE安全和非安全均开放) | 用户Flash开放,调试接口完全开放 |
| 0x2F7 | Provisioning | 当TZEN=0xC3时,调试正在被安装 | 不可变根安全(IROT)正在被安装 |
| 0x2EE | IROT-Provisioned | 当TZEN=0xC3时,调试在HDP_LNS中开放 | 不可变根安全已安装,不可回退到Open |
| 0x2FC | TZ-Closed | 调试限制在非安全区域 | 设计限制在非安全区域 |
| 0x2F2 | Closed | 关闭(需要安全认证进行调试访问) | 默认安全状态,调试需认证 |
| 0x2EC | Locked | 转换到其他状态被禁止,即使有调试认证也不允许 | 最高安全锁定状态 |
| 0x2FA | Regression | 转换到Open状态 | 由调试认证系统初始化的回退 |
| 0x2F8 | N2-Region | 转换到TZ-Closed | 由调试认证系统初始化的转换 |
在本方案中,核心涉及三种状态的转换:Open(初始状态)→ Provisioning(中间配置状态)→ Closed(最终运行状态)。Open状态下调试接口完全开放,BOOT0引脚控制启动路径,是芯片出厂的默认状态。Provisioning状态是一个过渡状态,专门用于执行OBKEY等安全配置的安装操作,此时芯片进入RSS区域执行安全服务。Closed状态是产品交付后的默认安全状态,调试接口需要通过安全认证(OBKEY)才能访问,启动路径由选项字节决定,BOOT0引脚不再起作用。
状态转换并非任意可行,而是受到严格的规则约束。例如,从Closed状态无法直接回退到Open状态,必须通过Regression流程并经过调试认证系统验证。Locked状态则禁止任何状态转换,即使拥有调试认证也无法更改。这些规则确保了产品状态的安全性与不可逆性,防止攻击者通过状态切换绕过安全机制。Product State的修改通过Bootloader的Special命令(0x50)配合更改Product State子命令(0x01)实现,命令参数中包含目标状态的代码值。
3. I2C Bootloader通信协议详解
I2C Bootloader通信协议是Host端与目标端之间交互的基础。根据AN4221应用笔记,STM32的I2C Bootloader采用标准I2C通信协议,目标端作为I2C从设备,Host端作为主设备发起通信。
在物理层参数方面,STM32H563的I2C Bootloader使用7位器件地址0x65。需要特别注意的是,在使用STM32 HAL库的I2C函数(如HAL_I2C_Master_Transmit)时,Slave地址参数应为7位地址左移1位后的结果,即0x65 << 1 = 0xCA。这是因为HAL库的地址参数包含了读写位,7位地址需要左移一位腾出最低位用于R/W控制。若直接传入0x65,将导致寻址错误,通信无法建立。
在引脚映射方面,参考工程使用PD12作为I2C_SCL时钟线,PD13作为I2C_SDA数据线。这两个引脚在NUCLEO-H563开发板上可用,且支持I2C复用功能。I2C总线的SCL和SDA线必须外接上拉电阻,这是I2C协议的物理层要求。若缺少上拉电阻,总线将无法正确释放,导致通信失败。典型的上拉电阻阻值为4.7kΩ,具体取值需根据总线电容与通信速率调整。
在协议层,I2C Bootloader遵循标准的命令-应答机制。Host端发送命令字节后,目标端返回ACK(0x79)或NACK(0x1F)表示命令是否被接受。对于需要数据传输的命令(如写Memory),Host端在收到ACK后继续发送数据,目标端每接收一个数据字节后返回ACK。命令执行完成后,目标端返回最终状态码。整个通信过程严格遵循AN4221中定义的时序要求,开发者在实现Host端时需严格按照文档中的时序图进行编程。
4. 固件升级命令集:0x44擦除与0x31写入
固件升级过程中,数据的写入涉及两个核心命令:Flash擦除命令(0x44)和写Memory命令(0x31)。这两个命令属于STM32 Bootloader的标准命令集,在AN4221中有详细的协议规范。
Flash擦除命令(0x44)用于在写入新固件前擦除目标Flash区域。STM32的Flash采用按页擦除机制,每次擦除一个或多个Flash页。Host端发送0x44命令后,需跟随擦除参数,包括要擦除的页数量及页编号列表。固件升级前,Host端需根据待下载固件的大小计算所需的Flash空间,确定需要擦除的页范围,然后发送0x44命令擦除对应页面。擦除操作是写入的前置条件,未擦除的Flash区域无法正确写入数据。
写Memory命令(0x31)用于将固件数据写入目标端的Flash或RAM区域。该命令的一个重要限制是每次最多支持写入256字节数据。因此,对于较大的固件文件,Host端需要将固件分割为多个256字节(或更小)的数据块,分多次调用0x31命令依次写入。每次写入时,Host端需指定目标起始地址和数据长度,目标端Bootloader接收到数据后将其写入指定地址的Flash或RAM。写入完成后,目标端返回状态码表示操作结果。
参考工程中的BOOT_Erase函数实现了Flash擦除命令的完整协议,BOOT_WriteMem函数则实现了写Memory命令。这两个函数封装了I2C通信的细节,包括命令发送、参数传输、ACK/NACK检测以及状态码解析,为上层应用提供了简洁的调用接口。开发者在移植时可直接参考这两个函数的实现逻辑。
5. Special命令(0x50)体系
Special命令(0x50)是STM32H5系列Bootloader特有的扩展命令集,用于支持Product State修改、OBKEY Provisioning、MCU复位等高级操作。这些操作不属于标准Bootloader命令,而是STM32H5安全架构的专属功能。根据AN2606与AN4221,Special命令的格式包含2字节命令码、2字节子命令码以及可变长度的数据字段。
| 功能 | Special命令 | 子命令 | 数据字段 | 接收/发送数据 | 数据字节数 | 状态 |
| 更改Product State | 0x50 | 0x01 | Product State代码 | 发送:状态值 | 0x01 | 0x00 |
| 复位 | 0x50 | 0x02 | Get | 0x00000000 | NA | 0x00 |
| 打开Provisioning | 0x50 | 0x03 | Get | 64位密码(两部分) | 0x02 | 0x00 |
| 关闭Provisioning(HDP_L=1) | 0x50 | 0x03 | Get | 64位密码(两部分) | 0x02 | 0x00 |
| Provisioning | 0x50 | 0x83 | Get | Provisioning命令/错误代码 | 0x01 | 0x00 |
更改Product State子命令(0x01)是本方案中使用最频繁的Special命令。Host端发送0x50命令码后,跟随0x01子命令码,再发送1字节的目标Product State代码值(如Provisioning状态代码0x2F7,Closed状态代码0x2F2)。目标端接收到命令后,修改选项字节中的PRODUCT_STATE字段,并返回状态码。需要注意的是,Product State的修改涉及选项字节的写入,操作完成后通常需要复位才能生效。
复位子命令允许Host端通过I2C总线远程复位目标端MCU。这一功能在升级流程的最后一步至关重要:在完成固件下载与Product State配置后,Host端发送复位命令,目标端执行系统复位,复位后根据新的Product State从用户Flash启动。参考工程中的BOOT_SP_ResetCmd函数实现了该命令。
Provisioning子命令(0x83)用于执行OBKEY的Provisioning操作。该命令需要配合写Memory命令(0x31)使用:首先通过0x31命令将OBKEY数据写入目标端的SRAM3区域,然后发送0x50 + 0x83命令,Bootloader接收到该命令后,将RAM中的OBKEY数据写入OBKEY Flash区域,完成Provisioning过程。
6. OBKEY Provisioning技术原理
OBKEY(Option Byte Key,选项字节密钥)是STM32H5安全架构中的重要组成部分,用于调试接口的安全认证。当Product State处于Closed状态时,调试接口被锁定,只有通过正确的OBKEY认证才能恢复调试访问权限。OBKEY Provisioning即是将OBKEY数据写入芯片专用OBKEY Flash区域的过程。
OBKEY文件由ST提供的工具生成,包含Header和Payload两部分。Header中包含了OBKEY的元数据与校验信息,Payload则是实际的OB密钥数据。然而,工具生成的OBKEY文件Header格式与Bootloader执行Provisioning时所需的Header格式并不一致,因此在将OBKEY下载到目标端RAM之前,需要按照RM0481参考手册中的要求重新计算Header。
Header重计算的核心是CRC-32校验。根据文档要求,CRC计算使用CRC-32算法,多项式为0x4C11DB7(Ethernet标准),初始值为0xFFFFFFFF。计算范围覆盖OBKEY数据缓冲区(pConfig->pData)中的Provisioning数据,不包含OBKey源数据。计算得到的CRC值写入Header的相应字段。参考工程中的Run_ProvisionCmd函数包含了Header重计算的完整逻辑。
OBKEY Provisioning的完整流程包含两个步骤:第一步,Host端通过写Memory命令(0x31)将处理后的OBKEY数据写入目标端的SRAM3内存区域;第二步,Host端发送Special命令(0x50)配合Provisioning子命令(0x83),Bootloader接收到该命令后,将SRAM3中的OBKEY数据写入OBKEY Flash区域,完成Provisioning。
关于OBKEY的版本选择,由于本方案基于TrustZone未使能的条件,因此必须使用带密码的OBKEY版本,即STM32Cube FW_H5 V1.5.1包中的DA_ConfigWithPassword.obk文件。带密码版本的OBKEY在Provisioning时需要提供64位密码,用于打开和关闭Provisioning状态。若使用无密码版本,在TrustZone未使能的条件下Provisioning将失败。
存放OBKEY数据的目标RAM必须位于SRAM3地址区域。这是因为Provisioning操作由RSS(根安全服务)执行,而RSS仅能访问SRAM3这一非安全区域的内存。若将OBKEY数据存放在其他RAM区域(如SRAM1或SRAM2),Provisioning命令将无法正确读取数据,导致操作失败。SRAM3的具体地址范围需参考RM0481参考手册中的内存映射章节。
7. 关键技术陷阱与规避策略
在实现STM32H5 I2C Bootloader固件升级方案的过程中,开发者可能遇到多个技术陷阱。以下对LAT1726文档中明确指出的常见问题进行归纳,并提供规避策略:
陷阱一:I2C通信缺少上拉电阻。I2C协议要求SCL和SDA线必须为开漏输出并外接上拉电阻。若Host与目标H5之间的I2C通信未接上拉电阻,总线电平将无法正确拉高,导致通信完全失败。规避策略:在硬件设计时,在SCL和SDA线上各接一个4.7kΩ上拉电阻至VDD;在面包板或飞线验证时,务必确认上拉电阻已连接。
陷阱二:I2C HAL函数地址参数未左移。STM32 HAL库的I2C主设备函数要求Slave地址参数为7位地址左移1位后的值。STM32H563 Bootloader的7位地址为0x65,因此HAL函数的地址参数应为0xCA(0x65 << 1)。若直接传入0x65,将导致I2C寻址错误,目标端无法响应。规避策略:在代码中定义地址时明确注释,使用(0x65 << 1)的表达式形式,避免硬编码错误。
陷阱三:OBKEY文件头格式不匹配。工具生成的OBKEY文件Header与Bootloader Provisioning所需的Header格式不一致,若直接下载原始OBKEY文件,Provisioning将失败。规避策略:在下载OBKEY到目标RAM之前,必须按照RM0481的要求重新计算Header,包括CRC-32校验值的更新。参考工程中的Run_ProvisionCmd函数提供了完整的Header重计算实现。
陷阱四:OBKEY数据未使用SRAM3空间。Provisioning操作要求OBKEY数据必须存放在SRAM3区域,若使用其他RAM区域,Bootloader无法读取数据。规避策略:在调用写Memory命令下载OBKEY时,目标地址必须指向SRAM3的地址范围;在代码中使用宏定义SRAM3基地址,避免地址硬编码错误。
陷阱五:TrustZone未使能时误用无密码OBKEY。本方案基于TrustZone未使能条件,必须使用带密码的OBKEY版本(DA_ConfigWithPassword.obk)。若使用无密码版本,Provisioning过程将失败。规避策略:明确项目的TrustZone配置状态,根据配置选择对应的OBKEY文件;在参考工程中,OBKey.s文件已嵌入正确的带密码OBK文件。
陷阱六:Product State切换顺序错误。Product State的转换必须遵循Open → Provisioning → Closed的顺序,且每步切换后需要确认状态已生效。若跳过Provisioning状态直接从Open切换到Closed,或在状态未生效时执行下一步操作,将导致后续流程异常。规避策略:严格按照七步流程执行,每步操作后读取并验证Product State值。
8. 内存布局与SRAM3使用规范
STM32H563具有多层次的SRAM架构,包括SRAM1、SRAM2、SRAM3等多个独立的RAM区域,各区域具有不同的安全属性与访问权限。在OBKEY Provisioning场景中,SRAM3扮演着不可替代的角色。
Provisioning数据在目标端RAM中的组织遵循特定的结构规范,包含以下元素:
| 结构元素 | 说明 | 约束条件 |
| pSource | 待Provisioning数据的地址 | 必须在SRAM3地址范围内(非安全区域) |
| pDestination | 存储Provisioning数据的目标地址 | 必须在OBKeys区域内 |
| Size | 待Provisioning数据的大小(字节数) | 必须为16的倍数 |
| CryptoAlgorithm | 加密算法标识 | RSSUB_DataProvisioning函数必须加密或不加密OBKeys |
| STM32H56xx:仅允许DwPSAAADU或DwCADAADU | ||
| DwTPSAAADU:通知RSSUB_DataProvisioning根据相关OBKey源数据加密 | ||
| DwCAAADU:通知RSSUB_DataProvisioning以明文编程OBKey源数据 | ||
| Crc | CRC校验值 | CRC计算使用CRC-32(多项式0x4C11DB7,Ethernet) |
| 初始值:0xFFFFFFFF |
pSource字段指定了待Provisioning数据在RAM中的起始地址,该地址必须落在SRAM3的地址范围内。SRAM3被设计为非安全区域(Non-secure alias),可被RSS安全服务访问。这一设计确保了Provisioning过程的安全性:即使Host端将数据写入SRAM3,RSS在执行Provisioning时也能安全地读取数据,而不会暴露给非安全世界的其他代码。
Size字段要求数据长度必须为16字节的倍数,这是由加密算法的块大小决定的。若OBKEY数据长度不是16的倍数,需要在末尾进行填充。CryptoAlgorithm字段指定了Provisioning过程中使用的加密策略,对于STM32H563,仅支持DwPSAAADU(加密)和DwCADAADU(明文)两种选项。Crc字段存储了Provisioning数据的CRC-32校验值,用于Bootloader验证数据完整性。
在实际实现中,Host端需要在本地内存中构建符合上述结构的Provisioning数据块,然后通过写Memory命令将其完整写入目标端的SRAM3区域,最后发送Provisioning子命令触发Bootloader执行数据搬移与校验。整个过程对内存地址的准确性与数据结构的规范性有严格要求,任何字段的错误都可能导致Provisioning失败。
180