STMCU小助手
发布时间:2023-12-11 14:02
|
01前言0 {3 Y3 Z& e0 O* f" K0 M9 D, c: b4 B 客户使用 STM32G070RBT6 给海外用户开发产品,由于当地新需求,产品需要增加安全启动的功能。但是由于 X-Cube-SBSFU 包提供的示例中,只有基于 STM32G071 的示例。客户因此询问该怎么移植。本文将讲解这个移植过程。9 ?! T! {" c g9 L( P% |+ G * d( }# L7 u" W E/ D 02基于STM32G070和STM32G071的SBSFU 实现差异; {6 p; R" L% V5 f. E. w 在正式讲解之前,我们首先来看一看 STM32G070 和 STM32G071 的 SBSFU 实现差异。 , S/ @! {$ T2 f$ H STM32G070 是一个 value line 产品,首先,我们要意识到,有一些安全特性,相比于STM32G071,它是没有的,比如:PCROP,BOOT_LOCK 和 Secure User Memory。那么,缺少了这些安全特性的 STM32G070,是否还能实现安全启动的功能呢 ? 答案是肯定的。我们先来看 PCROP,BOOT_LOCK,以及 Secure User Memory 在 STM32G071 上的 SBSFU 实现中所扮演的角色是什么?: U$ f# E2 H% F( B* e& u
1 A' X0 w" F& P# A& `+ b _) t2 t 图1.STM32G0 的 SBSFU 安全实现 / C( r. c# w$ {3 N3 }+ z 如上图,在 STM32G071 中,在安全启动的实现中,BOOT_LOCK 用来参与实现唯一启动入口,Secure User Memory 则用来参与实现信任根。PCROP 在安全固件升级实现中用来与MPU 配合实现密钥的安全存储,同时在安全升级过程中涉及到一些密钥的加解密操作,借助于Secure User Memory 和 MPU 的功能, 将 App 与 SBSFU 本身实现完美隔离。 ) h7 F) G4 Z3 F
图2.STM32G0 内存安全映射(运行 SBSFU 时) * T; U. m$ y0 z$ P! E+ }: ?- V, h 回到当前问题,一旦 BOOT_LOCK,PCROP,以及 Secure User Memory 缺少的情况下,这些功能还能实现吗? 7 p1 E: ^, _& D6 C! P2 ^8 c& Z 我们再来看下对于安全启动而言, 它需要实现哪些基本功能? 1> 不可更改不可绕过的一段启动代码 2> 每次复位必先执行安全启动代码 3> 验证系统配置的完整性1 F3 @) k) G9 ] • 时钟配置/ ]! w# n- g4 n9 S8 j; }8 ?: Y) r • 寄存器配置/ r8 w( Q K: `5 \& F: I! U • 存储器保护设置, ….% l ]$ C0 H+ K' q+ } 4> 启动信任根服务 • 通过密码学算法与密钥,校验 App 的完整性与合法性(来源可信,未经篡改) ( c& r8 [0 j) T" I5 y 这里需要注意地是,上面提到的某一项安全属性也只是参与实现某一项功能,比如BOOT_LOCK,它只是”参与”实现了唯一启动入口这个功能。从图 1 可知, 除了BOOT_LOCK,还有 RDP,那么在缺少 BOOT_LOCK 的情况下,RDP 是否也可以实现唯一入口启动的功能。很明显,在 RDP2 时,MCU 的入口是唯一的。也就是说,没有 BOOT_LOCK的参与下,RDP2 一样可以实现安全启动对唯一入口启动的需求。RDP2+WRP 就可以实现安全启动的前两条基本要求。而第三条基本要求,完全是 SBSFU 内的纯软件实现。第四条要求,通过 RDP2+WRP+MPU 也可以实现。其实, 在缺少了 PCROP,BOOT_LOCK 和Secure User Memory 后,STM32G070 的安全特性其实跟 STM32F4 差不多,我们不妨来看下STM32F4 是如何来实现 SBSFU 功能的。9 n6 ~( `* T0 |; n4 [# @
图3.STM32F4 的 SBSFU 安全实现 如上图所示,在 STM32F4 中,借助于 RDPL2,WRP,MPU 就实现了 SBSFU 的全部功能。 : ?# r3 A6 [3 ]! C/ T$ Q
图4.STM32F4 的 SBSFU 内存映射 到这里,我们完全可以确信,在缺少了 BOOT_LOCK,PCROP 和 Secure User Memory这些安全特性之后,STM32G070 完全可以按照 STM32F4 实现 SBSFU 的方式来进行! , b" [4 c% H1 A/ }7 r% F2 k 在确立了大方向后, 我们接下来看具体如何实现。7 M+ V- K6 C8 S# _9 w' y* v7 l$ x 03开始移植7 a3 V9 y9 q, h9 K 第一步 : 确保原始工程运行正常2 x3 j4 Y# w B 从 ST 官网上下载最新了 SBSFU 包(v2.6.1),打开STM32CubeExpansion_SBSFU_V2.6.1\Projects\NUCLEO-G071RB\Applications\2_Images目录,其下有三个工程,2_Images_SECoreBin(后续简称 SECoreBin 工程),2_Images_SBSFU(后续简称 SBSFU 工程),2_Images_UserApp(后续简称 UserApp 工程)。使用对应 IDE 按顺序依次编译, 然后将 SBSFU 工程生成的 bin 文件烧录到 NUCLEO-G071RB板内,打开 Tera Term 串口终端, 通过 Tera Term 烧录 APP 进去。目的是首先确认原始工程一切运行正常。接下来就开始修改了。 9 P9 b& N4 D; M1 H, Z 第二步 : 将与 BOOT_LOCK, PCROP, Secure User Memory 相关的宏全部关闭 打开 SBSFU 工程的 app_sfu.h 头文件,找到并关闭下面三个宏 : 7 [+ O6 {# k2 v7 t5 D2 y6 `
' b8 a7 e, Z. N/ B# y6 m6 p 重新依次编译 SBCoreBin,SBSFU,UserApp 三个工程,并重新测试通过。 至此, NUCLEO-G071RB 板上运行的是移除了 BOOT_LOCK, PCROP,Secure User Memory 三个安全特性后的 SBSFU 程序,这个原理上与 STM32G070 上原则上是一致的。接下来就是要移植到 NUCLEO-G070RB 板上了,剩下的就只有 STM32G070 与 STM32G071 的非安全特性方面的差异了。 9 h& l, T9 r1 ^2 f' D/ |5 q0 G 第三步 : 移植到 STM32G070RB 首先得准备下一块 NUCLEO-G070RB 板。接着将三个工程 SBCoreBin,SBSFU,UserApp 的 device 修改成目标 MCU STM32G070RB,然后将三个工程的 C++预定义宏STM32G071xx 修改成 STM32G070xx。 以 Keil 为例 :
图5.device 选择 STM32G070RBTx 6 ]1 i/ C; Y" h4 H; K2 ?9 O% x2 ~ I* O/ c' Y, |+ d
图6.C++编译宏修改 ; `4 \ l+ O; E& g; c R9 V* S6 d STM32CubeIDE 工程的 device 配置比较难修改,因为它原本是灰色的,不允许修改。但我们使用 UE 打开.cproject 一样可以强制修改 : # S+ S5 m9 z* f. u; S# ^/ D 打开对应的.cproject 文件,搜索关键字 G071,将以下几处替换成 G070(修改两处) :- q/ `. E/ Q- _1 T+ w 6 D9 n+ L2 K, I& n$ t( Q' | E
图7.STM32CubeIDE 下的 device 修改 6 v- m/ n' c T+ g% ?" y/ ] 修改完后,打开 STM32CubeIDE 工程,在其 MCU 和 Board 的配置也会有相应的变化:+ G) p! r; @$ _" K1 Q1 I2 r F 6 s. L, l/ l; z
图8.STM32CubeIDE 的 MCU 配置 由此可见,在 STM32CubeIDE 工程的 MCU 配置也可以做相应的修改了。这是一个小技巧。 至此,三个工程的工程配置都做完了相应修改。接下来的就是代码方面的修改了。$ K: d( W& w" |3 Q 首先 STM32G070 的时钟树是没有 PLLQ 输出的,因此,在 SBSFU 和 UserApp 这两个工程内找到 SystemClock_Config()函数,注释掉 PLLQ 的设置,如下所示 :& i8 Q3 h" Q" u" f7 q) t$ E: M2 h 2 U1 S& K0 }3 x, k; ~, z3 z
原先 STM32G071 的工程中的打印信息是通过 LPUART1 对应的 PA2,PA3 打印的,换成NUCLEO-G070RB 板后,其引脚虽仍然是 PA2,PA3 引脚用来串口打印,但是 STM32G070中是没有 LPUART1 这个外设的,需要换成 USART2,因此,其对应的代码修改如下 : 在 SBSFU 工程中, 打开 sfu_low_level.h 头文件 , 和 UserApp 工程的 com.h 头文件中: ( V/ M3 J) U Q# E3 r3 `' [
如上红色部分即为修改处。 ! Y, z0 z* e# ^ 至此,代码部分全部修改完成。重新按顺序依次编译 SBCoreBin,SBSFU,UserApp 三个工程,将 SBSFU 工程首先烧录到 NUCLEO-G070RB 板,然后通过串口终端,按提示,用 YModern 协议将 UserApp 对应的.sfu 文件烧录进去。整个流程都可以正常运行的。这说明软件框架基本已经 OK。接下来运行下 APP 中各种安全测试。 4 A5 J q. _, S 04测试安全保护特性 当程序跳入到 APP 后,显示如下界面5 `0 x/ l N* w) ~! N' k
图9.测试主界面 2 M6 } i5 ^' o. u9 }, }1 p/ i7 X0 n7 L. Q1 n: Y4 m$ _ ' Q8 [' S9 u7 }7 f$ G 当选择 2 Test Protection :Secure User Memory 时,结果会出错 : ) a9 v. k: u* a5 r+ J( g
图10.测试保护 4 @4 e! J4 Y. s9 @ 很明显,这里需要修改下,因为 STM32G070 是没有 Secure User Memory 的。查看其对应代码: f6 e0 L9 [% z( J; E
+ x$ c1 l- |2 r! o# k1 _3 t 原来 UserApp 是测试直接读取保存在 SECoreBin 内的密钥数据, 测试是否能读出。结果发现是可以的,因此,保护效果是出问题了。# O% P9 E' t* w! e+ d 首先我们修改下此函数,由于 STM32G070 中 Secure User Memory 是不存在的, 此函数叫 TEST_PROTECTIONS_RunSecUserMem_CODE()已经不再合适, 依照 STM32F4 的SBSFU 实现,将此函数换成 TEST_PROTECTIONS_RunSE_CODE()函数:4 g) J$ F+ t3 A& U5 t! k. k/ x * a% M8 `7 j( Y$ y7 A
( J1 o0 F; V1 O3 M; _# i 同样的尝试读取密钥:6 B4 @/ j$ s1 V5 p9 Y2 K
图11.UserApp 尝试读取密钥 测试结果可想而知, 肯定是可以读取的。于是得查看下为什么可以。 仔细查看图 2 的 STM32G071 的 SBSFU 原来实现中,SE Key 是通过 PCROP+Secure User Memory 来保护的,现在换成 STM32G070,原本 Secure User Memory 用来作隔离机制, 现在换成了 MPU, 而原本保护 SE Key 的 PCROP 在 G070 中压根就不存在,于是 SE Key 只剩下 MPU 来保护,因此,我们接下来得着重分析 MPU 对 SE Key 的保护。 - i" t7 h: s! u ?+ h( a1 D 为了方便调试,我们首先得将除 MPU 以外的所有保护通通关闭,只剩下 MPU 保护。于是在 SBSFU 工程中,在 app_sfu.h 头文件中,将以下宏通通注释掉:( D4 K; l/ t# ?/ v6 m4 |1 C
然后开始调试。在调试过程中,发现程序在从 SBSFU 跳转到 UserApp 之前,在代码中特意将 MPU 关闭: 如在 sfu_low_level_security.c 源文件中的内存函数 SFU_LL_SECU_ActivateSecUser()中有这么一行代码:
3 `( X- S* d! z 这就是为什么 UserApp 中仍然可以直接读取密钥的原因了。很明显,接下来我们需要将 SEKey 用 MPU 保护起来。但在这之前,我们得首先弄清楚,当程序跳转到 UserApp 后,Flash 和Ram 该如何设置 MPU 保护? " w( P2 H$ E# A0 T- ], E3 S6 o0 R 在 UM2262 中, 有提到当程序跳转到 UserApp 后 flash 和 RAM 的状态:: H' I# ~! t% P' a 6 r! N' c% ^/ r* b4 Q1 D# p3 \7 g! l
图12.UserApp 运行时的 flash 和 RAM 状态 % ?3 K7 P- q* ]* u2 t ~1 S: H从上图可以看出,原本 Secure User Memory 保护的区域,我们得使用 MPU 来替代实现相应功能,包含 SBSFU 整个代码和 Slot#1 内的 header 信息。这也就是在代码跳转到 UserApp之前需要做的事情。而黄色对应的 SRAM 区别已经擦除,当程序跳转到 UserApp 后其实已经没必要再保护。7 N* x3 L+ B& ~% y, A ! Q- J9 ?% ]# \7 y. ~# v1 i 于是在跳转到 UserApp 的内存函数中配置 MPU: 6 f2 w* C6 i7 _( V0 ^. `
7 h2 G/ o8 B+ u/ R: J/ Y
( ?5 z# u; a' X, s7 ]4 |4 t 注意这里是一个内存函数,它所调用的所有子函数也都是内存函数。在这个函数中我们将SBSFU 所在的 64K Flash,再加上 2K 的 Active Slot 的 header 信息保护起来。内存 RAM 我们并没有配置保护,因为跳转到 UserApp 后它就完全开放,且内容已经清空。 对应的, 我们在 UserApp 中增加对 Header 的测试函数 :, U4 z Z; L+ V' D6 B% a 6 U- z: [- t* X" j) w
3 X7 Z3 \5 K5 i7 z, o0 y 重新烧录程序,当程序运行后,选择 2 进行 protection 测试,然后再选择 2,测试访问SBSFU 所有的 64K 区域:
图13.测试 SBSFU 的 64K 代码区 5 v7 k/ |6 V# M l8 C[8 ^, v6 s$ v! n2 l) a; o6 ]7 ~ 发现会一直卡住, 这说明 MPU 对整个 SBSFU 64K 区域的保护已经生效。同样的,选择’3’测试 header 所在区域:, Z5 l$ q% X, U0 P% L2 j. ^0 p- M
图14.测试 Header 对应的 2K 区域 6 |4 D3 ?, u0 v3 ]0 O0 } 发现程序读取 0x0801 0000 地址时会一直卡住,这说明 MPU 对 header 的保护也已经生效。然后再测试了下其它选项,包含下载新固件,还有其它默认的一些安全特性,基本都是OK 的。这说明整个程序基本已经 OK。 2 Y: G7 v% }# }3 \ 8 K) w- s, U/ R* G5 D. H8 D 最后再恢复之前注释掉的除 MPU 之后的保护。 05后述& ]& u7 i% e! r 本文旨在通过一个相对容易的移植, 让读者对 SBSFU 的移植过程有一个大概了解以起到参考和示范作用。 P6 P. W) ^, a& A( j0 u F. ^ 如有侵权请联系删除5 \8 j3 y9 V$ {4 F& i 转载自:STM32单片机# K8 R2 y' n+ f 8 T% O% t9 d' U7 r" P+ v* L) U " X' K) z. z1 e& ]. i 8 j1 H' A3 v1 V$ a9 G $ P* T6 H. k6 Y2 z7 F |
实战经验 | 基于STM32G0B1的全桥移相+同步整流PWM解决方案
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:逢7发帖赢大礼】8、基于STM32G070板子的Ymodem串口协议IAP Bootloader开发
【福利三:逢7发帖赢大礼】7、基于STM32G070板子TF卡的FATFS移植
【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植
【福利三:逢7发帖赢大礼】5、基于STM32G070板子IRDA红外遥控接收解码
【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库
【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计
【福利三:逢7发帖赢大礼】3、基于STM32G070板子的YModem串口协议通信
【福利三:逢7发帖赢大礼】2、基于STM32G070板子的OLED移植U8G2库
微信公众号
手机版
不明觉厉,SBSFU还真没有涉猎。看来很多东西不知道。