
TF-A移植. B4 r- c3 V- S3 { 前面我们已经对TF-A进行了详细的讲解,但是当我们实际做产品的时候我们的硬件平台肯定会和ST官方的有区别,比如DDR容量会改变,自己的硬件没有使用到官方EVK开发板所使用的PMIC芯片等等。因此这里就涉及到将半导体原厂提供的TF-A移植到我们的硬件上,本章我们就来讲解一下如何将ST官方提供的TF-A源码移植到正点原子的STM23MP157开发板上。 ! Z) f2 `& m( ?, b6 N6 m. c: q 9.1 编译ST官方TF-A源码+ Z! O9 {+ C* W% n+ a 9.1.1 为什么要编译官方TF-A源码8 {$ k# ]6 b% D 所谓的移植就是让半导体官方提供的软件在自己的硬件平台上运行起来,准确的说应该是将自己的硬件添加到官方软件包。TF-A是ARM官方出品的一个软件包,半导体厂商会从ARM官方下载这个最正宗的TF-A软件包,然后将自己公司的SOC芯片添加进去,最终打包好提供给SOC用户,这个就是所谓的SDK包。我们在6.1.2小节对TF-A源码打的那个补丁就是ST编写的针对自家芯片的补丁文件,打完补丁以后的TF-A就支持了ST公司的芯片,所有使用ST芯片的厂商都会使用这个TF-A源码。打完补丁以后的TF-A能不能正常工作是需要验证的,也就是需要在一个硬件平台上运行测试,所有半导体厂商都会针对自家的芯片出一个或多个官方开发板。ST官方就有STM32MP157 EVK开发板,ST提供给用户的软件包都是在这个开发板上验证通过的,包括TF-A、uboot、linux kernel等。所有使用STM32MP157芯片的厂商都会在ST官方的开发板硬件基础上进行修改,制作自己的硬件,比如正点原子的STM32MP157开发板就参考了ST官方的STM32MP157 EVK开发板。 既然硬件参考了ST官方的开发板,那么软件肯定也是在官方提供的软件包基础上修改的,所以本章TF-A的移植就是在ST官方提供的TF-A源码上进行。首先肯定要先编译一下ST官方提供的TF-A源码,目的如下:5 Q+ K5 A- K5 M/ {1 w ①、掌握半导体官方软件编译方法,这个非常重要,编译都不会后面怎么可能做下去。1 j4 D( y4 i* d, V ②、验证开发环境搭建是否正确,比如交叉编译器设置是否正确,所依赖的第三方库有没有安装等。! f4 [- K% @ k; G ③、观察编译结果,比如编译完成以后的可执行文件保存在哪个目录下,都有哪些可执行文件,其区别是啥等。 ④、在自己的开发板上运行编译出来的可执行文件,所谓的移植就是改bug的过程,将编译出来的STM32MP157官方开发板可执行文件在自己的板子上运行,然后观察运行过程有没有错误,没错误最好(基本上不可能没错,除非完全参考原厂开发板设计的),有错误就改错误,直到能在自己的开发板上正常运行,这就是移植过程。 9.1.2 编译TF-A( C$ k" H" ^+ O1 `2 { 在6.1.2小节我们已经准备好了ST官方的TF-A源码,我们另外新建一个名为“my-tfa”的目录来存放并编译ST官方的TF-A源码,如图9.1.2.1所示: - j @& U* [9 e9 K/ b* W) W ![]() 0 e- C; {7 U+ a& ]% l 图9.1.2.1 TF-A源码文件 图9.1.2.1中我们重点关注Makefile.sdk和tf-a-stm32mp-2.2.r1,Makefile.sdk是我们一会编译要用到的Makefile,其实Makefile.sdk主要是配置编译选项,最终是通过调用tf-a-stm32mp-2.2.r1下的Makefile来完成具体编译过程的。tf-a-stm32mp-2.2.r1就是打完补丁后的ST官方TF-A源码文件,也就是我们要编译的。' k: c: d! r5 D 1、修改Makefile.sdk 首先要修改图9.1.2.1中的Makefile.sdk,因为此文件定义编译选项,包括交叉编译器,我们要使用自己的交叉编译器,也就是4.3.1小节安装的arm-none-linux-gnueabihf-gcc编译器。用VIM编辑器打开Makefile.sdk,找到“CROSS_COMPILE”,将其改为“arm-none-linux-gnueabihf-”,修改以后如图9.1.2.2所示:( w# W7 \$ ], B8 V1 q* U+ i7 Q ![]() " N5 m* ]6 Q4 I t. J V: Q: B 图9.1.2.2 修改后的Makefile.sdk 修改完成以后保存退出,进入到图9.1.2.1中的tf-a-stm32mp-2.2.r1目录下,然后进行编译,命令如下: ( G/ S2 L9 Q F( ~; ]- b( e6 l) K' @
如果需要加快编译速度,可是使用多线程编译,线程数最好和自己给虚拟机分配的物理核心一致,使用-j来指定线程数,命令如下:
编译完成以后会在tf-a-stm32mp-2.2.r1文件夹的上层目录(也就是Makefile.sdk同级目录下)下生成一个名为“build”的文件夹,build文件夹下就是编译生成的所有可执行文件,如图9.1.2.3所示: q1 P8 S9 C: F: h ![]() 图9.1.2.3 编译生成的build目录 进入到build目录下,有三个子目录,如图9.1.2.4所示: 4 m( }7 V0 f& c) z& Y( H @5 X7 ~ ![]() 图9.1.2.4 编译结果 图9.1.2.4中有3个子目录:optee、serialboot和trusted,说明有3个版本的可执行文件,optee是针对OP-TEE的,本教程没使用OP-TEE,所以optee目录和我们没关系。+ t. B$ S* b: L2 S9 h serialboot看名字就知道是和串行启动有关的,也就是通过串口或者USB烧写系统的时候需要用到serialboot下的可执行文件,我们在6.2.3小节编写STM32CubeProgrammer所使用的Flashlayout文件(.tsv)的时候用到了一个名为“tf-a-stm32mp157d-atk-serialboot.stm32”的文件,此文件就是serialboot目录下的。当然了,图9.1.2.4中目前还没有“tf-a-stm32mp157d-atk-serialboot.stm32”这个文件,这里只是给大家讲解一下xxx-serialboot.stm32这个文件的来源。当把TF-A移植到自己开发板上以后就会,就会在serialboot目录下生成我们板子对应的xxx-serialboot.stm32文件,后面会讲解。# {4 k4 h2 f5 L7 m! ]: m trusted目录下的就是烧写到开发板中的TF-A镜像,进入trusted目录,如图9.1.2.5所示:3 Z% Z: D4 p0 |! c ![]() + F' l% a6 i+ o! @( Q 图9.1.2.5 TF-A镜像文件 从图9.1.2.5可以看出,编译出来了很多后缀为.bin、.elf、.ld、.stm32这样的文件, 我们以前缀为“tf-a-stm32mp157d-ev1”的文件为例,相关的文件如表9.1.2.1所示: 文件 描述 tf-a-stm32mp157d-ev1.bin 使用STM32MP157D芯片的EV1开发板对应的TF-A二进制可执行文件。% G8 u( t8 |& X0 ^; E. `* ~ tf-a-stm32mp157d-ev1.elf 生成bin文件所使用的elf文件。: s& U+ _9 E/ O- y5 A tf-a-stm32mp157d-ev1.ld 连接信息9 H1 o" H* H; J) e8 s9 K tf-a-stm32mp157d-ev1.map 内存映射文件。 tf-a-stm32mp157d-ev1.stm32 添加好头部信息的bin文件,可以直接烧写到开发板中。' R1 N- e7 |' q' L tf-a-stm32mp157d-ev1-trusted.stm32 和tf-a-stm32mp157d-ev1.stm32文件一模一样,只是重命名了一下,为了和optee以及serialboot目录下的tf-a固件进行区分。! e/ t% _& z ]" Z; R0 E 表9.1.2.1 使用STM32MP157D芯片的EV1开发板对应的文件. K6 G! {2 p o- c3 U9 D 从表9.1.2.1可以看出,这些文件都是针对使用STM32MP157D芯片的EV1开发板,同理,其他的文件命名也是类似的,我们只需要选择和我们所使用开发板接近的文件就行了。比如正点原子STM32MP157开发板使用的主控型号是STM32MP157DAA1,开发板硬件参考了ST官方的STM32MP157 EVK开发板,因此我们就需要使用表9.1.2.1中的这些文件,在移植的时候需要参考的对象就是stm32mp157d-ev1。 另外,表9.1.2.1中的tf-a-stm32mp157d-ev1.stm32和tf-a-stm32mp157d-ev1-trusted.stm32这两个文件内容一模一样,后者只是前者的重命名版本,目的是为了和上一级optee和serialboot下的文件进行区分,我们再烧写的时候使用tf-a-stm32mp157d-ev1-trusted.stm32即可。 在trusted目录下,还有一些其他的文件,比如单独的bl2和bl32对应的可执行文件:bl2.bin和bl32.bin。bl2和bl32编译过程中所有的.c源码文件对应的.o文件也都保存在bl2和bl32文件夹下,感兴趣的可以去看一下。6 Q, B- H$ [& g0 c5 U" L" J 9.1.3 烧写ST官方TF-A镜像6 r1 Z" t3 q1 H6 N$ |( y# V) H 将上一小节编译出来的tf-a-stm32mp157d-ev1-trusted.stm32烧写到正点原子开发板EMMC里面,烧写方法参考6.2.3小节讲解的。烧写完成以后打开串口,设置好串口调试软件MobaXterm,设置开发板从EMMC启动,按下复位按键,观察MobaXterm软件接收到的log信息,如图9.1.3.1所示: : D- ^7 G6 T- o: r ![]() & j$ y& \: z( U- e5 j: s! Z 图9.1.3.1 运行结果+ S* l4 G* f7 P 图9.1.3.1的意思就是运行崩溃了,说明ST官方开发板对应的TF-A不能直接在我们的开发板上运行,需要进行修改。" ]6 Y3 z( G/ G P$ S 9.2 TF-A移植4 z3 c8 q$ x, {- E% K 经过测试ST官方开发板的TF-A镜像并不能直接在正点原子的开发板上运行,所以需要我们对TF-A进行修改,也就是移植到正点原子的开发板上。 注意,TF-A的移植全部是修改设备树,初学linux的朋友肯定对设备树(device tree)不了解,而设备树是一个系统的概念,会在后面的linux驱动开发里面详解设备树的相关语法!所以初学的朋友直接根据接下来的教程修改就行了,不用深入研究其原理。或者,直接跳过TF-A的移植,直接使用正点原子移植好的。 再次“鞭尸”ST,所有的软件全部采用设备树,导致初学者上手难度增大!!! 9.2.1 新建自己开发板对应的设备树 1、创建板子对应的设备树0 F! n. L1 K, ]0 x 设备树英文名字叫做Device tree,用来描述板子硬件信息的,比如你的板子上的CPU有几个核、每个CPU核主频是多少,IIC、SPI这些外设的寄存器范围是多少,IIC接口下都挂了哪些设备等等。设备树文件是一种文本格式的文件,方便阅读与修改,文件后缀为.dts,设备树也有头文件,头文件后缀为.dtsi。和C语言一样.dts可以引用头文件.dtsi,.dts经过编译以后生成.dtb文件,.dtb就是开发板要使用的。类似于我们将C语言的.c文件编译为.bin文件,然后在开发板上运行,将dts编译为dtb的工具叫做DTC,并不是我们使用的gcc编译器。: X" \% g) ?: D 打开tf-a-stm32mp-2.2.r1\fdts目录,fdts目录下保存的就是所有开发板的设备树文件,其中就包括了STM32MP1系列的,如图9.2.1.1所示: 3 y" H; K; C4 k( E* j ![]() 8 z* S& I+ f* _( |. d! v, A( F 图9.2.1.1 TF-A设备树文件5 r( p% W4 P& H: Y7 w. A 图9.2.1.1中的stm32mp157d-ev1.dts就是STM32MP157D EVK开发板所对应的设备树文件,同理还有STM32MP157其他型号的芯片,比如stm32mp157a、stm32mp157c等。我们以stm32mp157d-ev1.dts为蓝本,添加正点原子STM32MP157开发板对应的设备树文件。5 f2 F/ a1 K, s' r5 `8 `7 H5 Z 创建VScode工程,然后用VScode打开,打开stm32mp157d-ev1.dts文件,文件内容如图9.2.1.2所示: ![]() 图9.2.1.2 stm32mp157d-ev1.dts文件内容 图9.2.1.2中第8行引用了“stm32mp157d-ed1.dts”这样一个dts文件,注意,在设备树里面可以直接通过include引用另外一个.dts文件,不仅限于.dtsi文件,也就是说.dts文件也可以作为头文件使用。 可以看出stm32mp157d-ev1.dts文件非常简洁,主要原因是其引用了stm32mp157d-ed1.dts,主要工作都由stm32mp157d-ed1.dts文件来完成了。既然如此,那我们就把蓝本换为stm32mp157d-ed1.dts得了。复制一份stm32mp157d-ed1.dts,并命名为stm32mp157d-atk.dts,stm32mp157d-atk.dts就是我们为正点原子STM32MP157开发板准备的设备树,命令如下: cd fdts0 D! @" O; _- a; P8 o4 x" t cp stm32mp157d-ed1.dts stm32mp157d-atk.dts //复制" V& a1 M. M( H0 T 复制完成以后打开stm32mp157d-atk.dts文件,内容如图9.2.1.3所示: % i5 s6 `& r8 R2 H4 n3 H; X8 R ![]() 图9.2.1.3 stm32mp157d-atk.dts文件内容 第813行,头文件引用,其中第811行引用STM32MP15X芯片相关的dtsi头文件,这些全部保留即可。 第12行,非常重要,“stm32mp15xx-edx.dtsi”,看名字就知道,是edx系列开发板的通用头文件,适合具体板子有关的,很明显,板子不同其对应的板子头文件也不同。我们同样需要以stm32mp15xx-edx.dtsi为蓝本,创建正点原子开发板对应的板子头文件,一会再创建。 第20行,stdout-path表示标准输出,也就是设置TF-A信息输出路径,这里设置为serial0,也就是串行接口0(注意,不是STM32MP157的串口0),波特率为115200。* }. C5 \$ I( N. z2 i* T% O+ m4 F 第24行,设置serial0对应STM32MP157的串口4,所以TF-A会使用STM32MP157的串口4作为信息输出接口,大家在做板子的时候最好选择串口4作为通信接口。; \" [) H; y, h5 q* {# ]/ K5 E! @ 最后,我们上面说了,需要以stm32mp15xx-edx.dtsi为蓝本,制作正点原子开发板对应的头文件,方法很简单,直接复制一份stm32mp15xx-edx.dtsi,然后将其重命名为“stm32mp157d-atk.dtsi”,命令如下:$ l5 h% O2 i8 R8 l; O cd fdts cp stm32mp15xx-edx.dtsi stm32mp157d-atk.dtsi 拷贝完成以后就需要修改stm32mp157d-atk.dts,将第12行改为: #include “stm32mp157d-atk.dtsi” 修改以后的stm32mp157d-atk.dts文件内容如下(有缩减): ; W" `- ?4 j" Q
重点是修改后的第12行。 2、编译 设备树创建好以后编译一下,在编译之前要先修改以后Makefile.sdk,因此我们前面新建的stm32mp157d-atk.dts并没有添加到编译列表里面,直接编译的话并不会编译此文件。打开Makefile.sdk,在TFA_DEVICETREE配置项中添加“stm32mp157d-atk”,如图9.2.1.4所示: : u. F% V$ |7 z% z7 s2 b ![]() : |! G! P; y5 a4 q 图9.2.1.4 修改后的TFA_DEVICETREE 修改完成以后保存退出,然后执行如下命令编译一下TF-A:( u3 L M7 g! g4 ]
如果修改没有错误的话就会编译成功,编译完成以后进入build/trusted目录下,此时就会生成以“tf-a-stm32mp157d-atk”开头的相关文件,如图9.2.1.5所示: ![]() 图9.2.1.5 编译生成的正点原子开发板TF-A固件/ X& L7 t+ r/ h ^7 x5 F 图9.2.1.5就是我们以后要烧写的TF-A固件,其设备树(dtb)已经替换为我们上面新建的stm32mp157d-atk.dts了。但是由于我们并没有对TF-A源码做什么修改,所以tf-a-stm32mp157d-atk-trusted.stm32依旧是不可运行的,错误类型和图9.1.3.1中的一样,大家可以自行烧写测试。% W6 y( L B8 t" [0 `! u 9.2.2 修改设备树电源管理 ST官方STM32MP157开发板用到了一颗PMIC芯片,型号为STPMIC1A,PMIC全称为Power Management IC,也就是集成电源管理芯片。我们在用单片机的时候,只需要给一个3.3V或5V电源即可,使用起来很方便。但是随着芯片性能的提升,SOC对电源的要求越来越多,比如Cortex-A系列芯片主电源一般要求3.3V,但是芯片内核电压可能需要1.2V,而外扩的DDR3L芯片工作电压是1.35V,DDR3L芯片的端接电压要求0.65V,USB工作电压又要求5V等等,也就是说随着SOC功能越来越强大,所需要的的电源要求也越来越高,最直观的就是电源种类很多。另外,这些电源的上电顺序也是有要求的,也就是说,哪些电源先启动,那些后启动等。 为此,SOC厂商为了简化硬件设计,都会针对自家的SOC芯片推出专用的PMIC芯片,PMIC芯片就是一个单进多出的电源芯片,比如输入5V,然后可以输出很多路电源,输出的这些电源可以配置输出电压。STPMIC1A就是ST专门为STM32MP1系列设计的专用PMIC,此PMIC芯片性能非常强大:# `+ Z3 f4 W0 B4 \ · 输入电压2.8V~5.5V。/ p2 y0 q) z0 E$ m& T · 4路可调的通用LDO输出。 · 1路DDR3端接LDO电源。 · 1路USB PHY所使用的LDO电源。 · 1路DDR参考电压LDO电源。' {2 O0 p8 D7 r2 S" d · 4路可调的BUCK开关电源。4 q: P' a3 B% L# Z! s3 [$ { · 1路5.2V/1.1A的BOOST开关电源。: m" O; s; p9 f. W6 g · 1路500mA的USB OTG电源。 A k) `) g. z4 d& c · 1路500mA/1000mA的通用电源。 · 此芯片有一个IIC接口,主控通过IIC接口来配置电源芯片,设置每一路输出电源的输出电压,开启时间等。 STPMIC1A是个好东西,但是好东西是要付出代价的,STPMIC1A在ST官网报价1.9美金/1K(2020/11/12日),也就是说1000片起的话,价格是1.9美金,换算成人民币就是12.5块钱。但是对于STM32MP157而言用不了这么多路电源,三四路就够了,所以可以采用分离电源,这样的话三四个电源芯片的价格远小于12.5块钱。难点就是分离电源很考验硬件工程师的设计功底,因为处理不同电源上电顺序,好在ST已经提供了分离电源设计手册。 总之就是,因为采用ST提供的专用PMIC成本太高,因此大部分STM32MP157核心板都是采用分离电源设计的。STPMIC1A是一个IIC器件,因此就涉及到驱动问题,需要主控通过IIC接口来配置各路电源,所以ST官方提供的TF-A、uboot、linux kernel等都会有PMIC驱动。但是正点原子的开发板并没有采用这个PMIC芯片,所以TF-A默认的电源配置就会出问题,这里就需要我们修改相关的文件,取消掉这个PMIC的相关配置。 打开stm32mp157d-atk.dtsi文件,找到如下所以代码:. i9 } `, G, ~7 z
简答给大家说一下上述代码的含义: 第45行,“i2c4”表示这段代码是和I2C4控制器有关的,在设备树中一个IIC接口下的所有设备都“打包”放到一个节点下,在这里就是描述I2C4这个IIC控制器下的所有IIC器件。ST官方的STM32MP157开发板将STPMIC1A这个PMIC芯片挂到了I2C4下,所以STPMIC这个芯片的相关信息就会在“i2c4”这个节点下进行描述。 第54~281行,这一大段为STPMIC1A这颗芯片的描述信息,第54行是“pmic: stpmic@33”,即使我们没有学习过设备树,但是也能看出这是和PMIC有关的。在这一大段代码中,描述了STPMIC1A各输出电源应该如何配置,比如电压应该是多少,每路电源的功能是什么等。0 a9 B0 b/ m, h2 K# ^- W5 w 将示例代码9.2.2.1中第54~281行的内容全部删除掉,也就是删除掉STPMIC1A芯片的相关描述,删除以后的i2c4节点内容如图9.2.2.1所示:3 B7 L) Q' }" R* |4 h ![]() 图9.2.2.1 删除pmic信息后的i2c4节点7 ~3 q2 e# ~3 U 由于我们上面把PMIC相关的信息都删除了,但是其他设备的电源信息还是要有的,只是不通过PMIC来描述了。所以需要我们自己添加一些设备的电源描述信息,大家在stm32mp157d-atk.dtsi文件里面找到“vin”节点,如图9.2.2.2所示: ![]() 图9.2.2.2 vin节点内容$ t% Z: {6 t+ y3 Y 将图9.2.2.2中的vin节点内容全部删除掉,也就是将17~23行代码删除掉,替换为下面的代码: # x/ y) R" Y( N% i! y; \! I
第17~24行,描述VDDCORE电源,也就是STM32MP157的内核电源,最小为1.2V,最大为1.35V。) v8 n0 t( l; O0 x/ q 第26~33行,描述3.3V电源,最小和最大都是3.3V 第35~42行,描述VDD电源,这是一个3.3V的电源,所以最小和最大都为3.3V 第44~51行,描述VDD_USB电源,为3.3V,所以最小和最大都为3.3V。% c6 b; i, [8 K y2 \ 修改完成以后如图9.2.2.3所示: ; E+ e' c0 d2 y3 u: y ![]() 图9.2.2.3 修改后的电源信息 至此,电源管理修改完成了,不要编译,会报错。因为修改了电源设置,而USB OTG节点还是引用的以前相关电源设置,所以编译会报错,等修改好USB OTG设备节点以后就可以编译了。/ I8 }7 C0 [. Q: E/ y 9.2.3 修改TF卡和EMMC设备树4 C2 W& F( {6 |& b9 u 继续操作stm32mp157d-atk.dtsi这个文件,找到“sdmmc1”和“sdmmc2”这2个节点,节点内容如图9.2.3.1所示:. Z/ B3 i, `/ g1 r1 H ![]() 图9.2.3.1 sdmmc1和sdmmc2原节点内容9 O7 V6 t/ T \/ N# | 将图9.2.3.1中的sdmmc1和sdmmc2节点改为如下所示内容:& W! o' ^3 D/ _. l# M* |
修改完成以后sdmmc1和sdmmc2如图9.2.3.2所示:; l! x7 G. F5 b: {6 Q# c5 J 3 q$ a* U! O" ^' B ![]() ( ?# Y1 m {% K' z 图9.2.3.2 修改后的sdmmc1和sdmmc2节点 同样,不要编译,会报错,因为修改了电源,而USB OTG节点还是引用的以前相关电源设置,所以编译会报错,等修改好USB OTG设备节点以后就可以编译了。1 m* s* y! u0 E% X 9.2.4 修改USB OTG设备树 最后就是修改一下USB OTG对应的设备树节点,继续操作stm32mp157d-atk.dtsi,找到“usbotg_hs”这个节点,节点默认内容如图9.2.4.1所示: 5 u" M) S5 }- P$ B1 Q9 z ![]() 图9.2.4.1 原来的节点内容! N+ N* j6 q4 W4 r: { 将图9.2.4.1中的usbotg_hs节点改为如下所示内容: * y$ ^& l" H3 e+ X* P2 @# K
最后还需要修改“usbphyc”节点里面的“status”属性值,看起来就是向stm32mp157d-atk.dtsi文件里面添加了一个名为“usbphyc”的节点,内容如下:/ \* M/ v& G, E/ e
修改完成以后的usbotg_hs和usbphyc这两个节点如图9.2.4.2所示:5 R/ q f2 M' x ![]() 图9.2.4.2 修改后的usbotg_hs和usbphyc$ }; Q. v( Z p, T0 b2 ` 9.3 编译测试& R1 `, p$ X4 [/ r3 V 9.3.1 编译3 N8 T8 X" x% D* w, ~; U# j4 b0 a 设备树修改完成以后先编译一下TF-A,但是我们编译的时候默认会编译很多STM32MP1开发板的TF-A镜像,我们可以修改Makefile.sdk固件,只编译正点原子开发板对应的TF-A镜像。打开Makefile.sdk文件,将TFA_DEVICETREE修改为如图9.3.1.1所示内容:. y) h3 c# u$ `9 e. ^ T7 B : h0 R( d; v* c! L ![]() " s/ ~, \3 ]2 u 图9.3.1.1 修改后的TFA_DEVICETREE 图9.3.1.1中的TFA_DEVICETREE用于指定需要编译的板子对应的设备树,这里改为之编译stm32mp157d-atk,也就是正点原子开发板对应的设备树,也就是前面我们自己创建的stm32mp157d-atk.dts文件。# C& F" @1 o/ Q4 @0 _ 修改好以后重新编译TF-A,如图9.3.1.2所示,表示编译成功:& {2 x. V$ h- q, d % p Y M6 U D, f ![]() 5 ~0 d5 s, P) h& B! ?. S7 ^ 图9.3.1.2 编译成功% V! }- I5 w. u+ N 图9.3.1.2中提示tf-a-stm32mp157d-atk.bin编译成功,而且也生成了tf-a-stm32mp157d-atk.stm32镜像文件。 9.3.2 烧写测试 使用STM32CubeProgrammer软件将上面编译出来的build/trusted/tf-a-stm32mp157d-atk.stm32烧写到开发板上的EMMC中并运行,运行log信息如图9.3.2.1所示: 8 F; Z# F3 e; l( [7 z0 g8 Y ![]() 图9.3.2.1 TF-A运行log信息 从图9.3.2.1中可以看出TF-A运行成功,其中bl2和bl32(sp_min)都正常运行,bl2和sp_min的编译时间都为2020年11月23号,其中bl2编译时间为9:18:37,sp_min的编译时间为9:18:48,bl2先编译,SP_MIN后编译。细心的朋友可能会发现,bl2其实已经将uboot加载到了0xc0100000地址处,这是因为大家拿到的开发板默认已经烧写了出厂系统,所以TF-A可以加载uboot(bl33)。. p: a% P: S$ C5 b7 k. a3 D# m 这个时候uboot可能会启动,也可能不会启动,这里先不要管uboot能不能启动。一会我们会讲解如何把uboot镜像也烧写进去,用来测试TF-A是否工作正常。& h; M: }! h C: s 另外,由于正点原子的STM32MP157开发板出厂已经烧写系统,所以bl2可以加载uboot到DDR上,但是如果你用的是全新的核心板,EMMC还没有烧写过任何系统,或者你把EMMC格式化过,因为EMMC里面没有uboot镜像,所以bl2在加载uboot的时候就会失败,TF-A运行的log信息如图9.3.2.2所示: , r: B/ f6 f8 N- R* A8 r# \ ![]() 图9.3.2.2 TF-A运行错误' C/ m* l9 i' X1 M/ y& c/ _2 A 从图9.3.2.2中可以看出,TF-A提示没有找到ssbl分区(ssbl分区用于存放uboot),因为是全新的EMMC核心板,而我们烧写的时候也没有在FlashLayout中添加ssbl分区信息,所以会提示找不到ssbl分区,从而导致TF-A启动出问题。解决方法很简单,修改FlashLayout,然后在里面添加一个ssbl分区,但是ssbl分区不烧写任何文件,就相当于新建一个名为“ssbl”的分区。打开6.2.3小节中我们创建的FlashLayout文件tf-a.tsv,在最后一行添加如下内容:3 m& s+ |9 f8 ]) c' @9 _& j 示例代码9.3.2.1 向tf-a.tsv添加ssbl分区信息 PE 0x06 ssbl Binary mmc1 0x00080000 上面这一行“PE”表示不更新ssbl分区,但是如果此时还没有ssbl分区的话会先创建一个ssbl分区。其实就是在EMMC里面新建一个空的ssbl分区而已,添加完成以后的tf-a.tsv内容如图9.3.2.3所示:% ^4 q! B0 s# J9 \ 7 h8 D& h; B& ^8 w( }& _ ![]() , Z* P2 T/ F! \9 ` ^$ T7 i. m0 J) L 图9.3.2.3 修改后的tf-a.tsv文件 图9.3.2.3中最后一行就是ssbl分区,注意0x00080000后面的箭头表示TAB键,这个TAB键是必须要的!因为最后面还有Binary列的内容,这里我们并没有给出Binary列的镜像文件名,表示不烧写任何文件,但是位置得留出来,否则烧写的时候STM32CubeProgrammer会tf-a.tsv语法错误。 修改完成以后使用新的tf-a.tsv来烧写TF-A镜像,烧写完成以后TF-A运行log信息如图9.3.2.4所示: ![]() 图9.3.2.4 TF-A运行结果- p. L I( U7 N: v 从图9.3.2.4可以看出,此时没有再提示ssbl分区错误了,但是有另外一个错误,那就是在加载ID=5的镜像的时候加载错误,这个就是uboot镜像。因为全新的EMMC并没有uboot镜像,因此肯定加载不到,除非把uboot镜像也烧写进去,也就是烧写到上面的ssbl分区里面。. q- u0 h1 c1 o7 [. w$ ? 修改tf-a.tsv文件,在ssbl分区添加要烧写的uboot镜像即可,这里直接烧写正点原子官方出厂系统的uboot镜像即可,也就是u-boot.stm32,这个文件已经放到了我们烧写目录里面,修改后的tf-a.tsv文件如图9.3.2.5所示: / I7 H8 f- i* r1 Q& I 图9.3.2.5 修改后的tf-a.tsv 图9.3.2.5中最后一行的Opt要改为‘P’,也就是更新ssbl分区中的文件,u-boot.stm32要和tf-a.tsv放在同一个目录下。修改完成以后使用新的tf-a.tsv烧写TF-A和uboot镜像到开发板的EMMC里面,烧写完成以后运行TF-A,运行过程的log信息如图9.3.2.6所示: / k% Q% k( [& j3 N6 a, ^. \( h* e ![]() * l7 j t1 f1 N% t3 B" C ~ 图9.3.2.6 TF-A运行信息2 h7 Z7 h! a# ^4 e# i* ? 从图9.2.3.6可以看出,TF-A运行正常,而且因为我们也烧写了uboot镜像,因此uboot也启动了,说明TF-A运行正常。 9.3.3 xxx-serialboot.stm32测试 在6.2.3小节中我们构建的系统烧写目录中,用到了正点原子出厂镜像中的stm32mp157d-atk-serialboot.stm32,前面讲了,STM32CubeProgrammer首先通过USB或串口向开发板下载stm32mp157d-atk-serialboot.stm32,名字里面有个“serialboot”,翻译过来就是串行启动,此镜像主要用于初始化DDR,并且提供USB或串口功能,目的是为了进一步将uboot镜像下载到DDR的指定位置,最终通过uboot来向外部flash设备烧写整个系统镜像。- W, X. R$ n$ S& @ j 很明显我们也需要掌握自行编译stm32mp157d-atk-serialboot.stm32的方法,操作很简单。首先打开Makefile.sdk,然后将EXTRA_OEMAKE_SERIAL改为如下内容:" z. Z' ]1 E2 F 示例代码9.3.3.1 EXTRA_OEMAKE_SERIAL编译选项
修改完成以后如图9.3.3.1所示:2 r8 [8 h S9 f. h+ I4 P8 P* m 6 U% q& B+ [5 y/ _* M! N ![]() * ?, w! W% Y6 s! @7 j 图9.3.3.1修改后的EXTRA_OEMAKE_SERIAL配置选项 输入如下命令编译:1 K5 g1 k2 c3 b
编译完成以后就会在…/build/serialboot目录下生成tf-a-stm32mp157d-atk-serialboot.stm32,如图9.3.3.2所示: 图9.3.3.2 编译生成的tf-a-stm32mp157d-atk-serialboot.stm32文件 用图9.3.3.2中的tf-a-stm32mp157d-atk-serialboot.stm32替换我们以前使用的正点原子出厂系统中的文件,替换完成以后的烧写目录如图9.3.3.3所示: * }* ?1 v. ^& P, k7 S6 \# d6 t ![]() \. C5 E6 w7 Z7 t5 y7 o1 } 图9.3.3.3 images目录: m6 b3 t& g& h 此时图9.3.3.3中的tf-a-stm32mp157d-atk-serialboot.stm32和tf-a-stm32mp157d-atk-trusted.stm32均为我们自行编译的,tf-a-stm32mp157d-atk-trusted.stm32经过测试运行正常,接下来就要测试一下tf-a-stm32mp157d-atk-serialboot.stm32是否能正常烧写系统。 直接用STM32CubeProgrammer烧写测试即可,观察是否能正常烧写,烧写完成以后tf-a-stm32mp157d-atk-trusted.stm32是否能够正常启动。+ X# q" M0 d s 注意!按照本小节讲解的方法编译生成tf-a-stm32mp157d-atk-serialboot.stm32以后,…/build/trusted和…/build/optee目录就没了,也就是说此本小节的方法只能编译tf-a-stm32mp157d-atk-serialboot.stm32。一旦编译成功并测试OK以后,大家就不要再去修改stm32mp157d-atk-serialboot.stm32了。如果想重新编译tf-a-stm32mp157d-atk-trusted.stm32,那么就按照以前的编译命令即可,不用修改Makefile.sdk文件! m6 O1 R3 s/ Y W: V 至此,TF-A移植就全部完成。, I3 |1 ]. `6 I% e ———————————————— P; @- f) m2 j8 u C 版权声明:正点原子 $ n/ z$ a" Z0 F$ O5 j; Z4 y: |) V |
基于STM32MP1和STM32MP2在嵌入式Linux平台上部署有效的安全保护机制
利用STM32MP1和STM32MP2为嵌入式Linux提供有效的安全措施:供当今决策者参考的3条宝贵经验
STM32MP1 WiFi连接
【STM32MP157】从ST官方例程中分析RPMsg-TTY/SDB核间通信的使用方法
【STM32MPU 安全启动】 TF-A BL2 TrustedBoot原理学习
《STM32MPU安全启动》学**结
《STM32MPU安全启动》学习笔记之optee 如何加载CORTEX-M核和使能校验
《STM32MPU安全启动》学习笔记之TF-A BL2校验optee和uboot的流程以及如何使能
《STM32MPU 安全启动》课程学习心得+开启一扇通往嵌入式系统安全领域深处的大门。
《STM32MPU安全启动》 课程学习心得