
5.1 STM32MP1启动模式 STM32MP1支持从多种设备启动,比如EMMC、SD、NAND、NOR、USB、UART等。STM32MP1内部有一段ROM来存放ST自己编写的程序,这段ROM空间是不开放给用户使用的,仅供ST存放自己的ROM代码,ROM空间如图5.1.1所示:- { \& u' W4 z) \1 X$ k# n4 c: r ![]() 图5.1.1 内部ROM空间 图5.1.1中CA7是Cortex-A7的缩写,可以看出A7内核有128KB的ROM空间,起始地址为0X00000000,STM32MP1上电以后会先运行这段ROM代码。STM32MP157有三个BOOT引脚:BOOT0~BOOT2,这三个BOOT引脚通过拉高/拉低来设置从哪种设备启动,正点原子STM32MP157开发板上的拨码开关就是控制这三个BOOT引脚的,大家可以打开底板原理图,找到BOOT相关原理图,如图5.1.2所示: ) {1 r# ?/ b8 v" t6 A7 H& ` ![]() + n; a5 k" \" L1 C9 } 图5.1.2 BOOT原理图6 l$ J+ J" W+ j/ P& W 从图5.1.1可以看出,当BOOT0~BOOT1拨到“ON”的时候就会接到3.3V上,此时就是 逻辑1,拨到OFF的时候BOOT0~BOOT1就悬空(也可以外接下拉电阻),此时就是逻辑0(悬空或接地都为逻辑0),三个引脚不同电平对应的启动模式,如表5.1.1所示:; o9 O. o: M+ X8 @* G( w5 P BOOT2 BOOT1 BOOT0 启动模式 描述0 ^' V) ?/ {0 x8 c2 y# W% C 0 0 0 UART/USB - USART2/3/6、UART4/5/6/7/8。) O! X7 U0 s' {3 u8 ?' @ USB接口。 0 0 1 串行NOR 串行NOR或者QUADSPI( s( T" e( S3 e7 c 0 1 0 EMMC 连接到SDMMC2上的EMMC设备。$ B3 {9 s3 J% i7 n& [6 x7 x1 y 0 1 1 并行NAND 连接到FMC上的并行SLC NAND$ x/ C9 n% H3 r+ z 1 0 0 MCU 启动内部的M4内核。8 r( r! K) z% n; v" x9 W) t7 Z% F/ K 1 0 1 SD 连接到SDMMC1上的SD卡。7 w# N' [; z* q5 L& J 1 1 0 UART/USB 和000效果一样,从UART/USB启动。 1 1 1 串行NAND 连接到QUADSPI上的串行NAND1 A; L' r* c- i2 S: P+ ^+ e 表5.1.1 启动模式 注:STM32MP1的引脚具有复用功能,因此一个外设有很多不同引脚可以使用,比如SDMMC2的D0引脚就可以使用PB14或PE6。STM32MP1内部ROM代码肯定会有个默认的引脚,比如内部默认使用PB14,如果你自己绘制的板子用了PE6,那么就会出问题。当然了,可以通过修改OTP来设置启动设备所使用的引脚,但是笔者强烈不建议用户修改OTP,因为OTP只能改一次,一旦修改错误芯片就废了。所以大家在绘制自己板子的时候启动设备所占用的引脚一定要和ST官方的开发板一致!0 _' i* O2 s( J2 U; | 正点原子STM32MP157开发板上通过拨码开关来选择启动模式,开发板上有丝印提示如何选择不同的启动方式,如图5.1.3所示: ![]() 图5.1.3 开发板拨码开关设置5 |' ]3 Y$ K/ t1 {% {5 u: H 大家可以根据开发板上的丝印来选择启动模式,拨上去为ON,也就是‘1’,拨下来就是OFF,也就是‘0’。 但是要注意一点,开发板拨码开关3个开关从左到右依次对应:BOOT0,BOOT1和BOOT2,因此图5.1.2中丝印从左到右依次是BOOT0、BOOT1和BOOT2,顺序刚好和表5.1.1是相反的,表5.1.1中从左到右是BOOT2、BOOT1和BOOT0,所以大家看到开发板上丝印和ST数据手册里面不同的时候不要以为是丝印标错了。$ H$ c- u: A5 j% W; O9 o9 a . z3 W& W8 ?% [" L 5.2 STM32MP1启动流程详解1 m% @9 O6 [9 c3 o4 g" ?* Q9 p 上一小节我们说了,STM32MP1内部有一段ST自己编写的ROM代码,这段ROM代码上电以后就会自动运行,ROM代码会读取BOOT0~BOOT2这三个引脚电平,获取启动模式信息,比如读取到是从EMMC启动的,那么ROM代码就会从EMMC中读取相关程序。说起来很简单,但是实际操作比较复杂,本节我们就来详细学习一下这个启动过程。 5.2.1 内部ROM代码 内部ROM代码支持如下功能:2 L: l' x: [# I. }" ^: r d! o ①、Secure boot(安全启动),不管是串行启动还是从Flash设备启动。 ②、Engineering boot(工程启动?),当BOOT2~BOOT0设置为100的时候,我们就可以通过STLINK访问A7或者M4内核。一般是通过此方法来调试M4内核代码。 ③、Secondary core boot(第二个内核启动),复位以后,STM32MP157的每个A7内核都会启动,并且运行相同的指令。内部ROM代码会分离执行流,只有Core0才会运行ROM代码,另外一个内核会处于一个死循环状态,等待应用程序发送信号来进行下一步操作。这个信号是由SGI(软中断)和另外两个BACKUP寄存器:MAGIC_NUMBER、BRANCH_ADDRESS组成的。如果要启动Core1,运行在Core0的应用程序需要:& \: V# y5 y& Q& Y v% c · 将跳转地址写入BRANCH_ADDRESS寄存器。 · 将0xCA7FACE1这个值写入到MAGIC_NUMBER寄存器。9 {! I8 C. v% `* r; b& |2 G& s( e$ J · 向Core1和发送SGI中断。4 Y/ {4 f; C4 V4 u I- \- r 总结一下,只要你不自己开启第二个核,那么由于内部ROM代码的作用,此时STM32MP157就相当于单核A7。这样有利于我们编写的STM32MP157的A7裸机例程,因为无需考虑多核情况。 ④、RMA boot,RMA是Return Material Authorization的缩写,笔者没研究过此种启动方式。: K% T$ v R: A% j+ `" Q* p ⑤、低功耗唤醒。 ⑥、提供安全相关服务。" c7 o9 j+ H/ `3 J( j 内部ROM启动流程如图5.2.1.1所示: ![]() 图5.2.1.1 ROM流程图 我们只关心图5.2.1.1中红色部分,因为这个是最常用的启动流程,也就是上电或复位以后运行流程:* O) l# N! r4 g J ①、首先检查当前是不是CPU0运行,如果不是的话就启动CPU1,正常上电肯定是CPU0在运行。 ②、如果是CPU0在运行,检查复位原因。 ③、检查是否为退出Standby而导致的复位,如果不是的话就进入RMA检查。 ③、检查是否为RMA启动,不是的话是否为ENGI启动。 ④、如果不是ENGI启动的话直接进去冷启动。 ⑤、进入冷启动以后就从flash中加载系统,并且进行鉴权,如果鉴权成功的话就运行系统。 ' R7 w, D1 L/ S8 ?- b' A+ Y! e 5.2.2 安全启动 首先了解两个概念: FSBL:全称为First stage boot loader,也就是第一阶段启动文件。$ _- N3 {( i, S SSBL:全称为Second stage boot loader,也就是第二阶段启动文件。 当我们设置好BOOT2~BOOT0,选择从外部Flash,比如EMMC、NAND或NOR等启动的时候就会进入安全启动流程。STM32MP157的安全启动流程比较复杂,这里我们就简单了解一下安全启动的基本流程: M6 T- R5 U" k6 b5 ]! r& U7 B ①、首先ROM代码从选定的Flash设备中加载FSBL镜像文件,FSBL镜像就是ROM加载的第一个用户编写的可执行程序,一般是TF-A镜像,但是我们可以换成自己编写的程序,比如A7裸机代码。当然了,这个FSBL镜像是有要求的,不是简单的把bin文件丢过来就可以,而是需要在bin文件前面添加一个头部信息,否则内部ROM代码不知道如何处理这个bin文件,这个头部信息以后稍后讲解。 ②、FSBL镜像加载以后需要对其进行鉴权。 ③、如果鉴权成功,那么就会跳转到FSBL镜像入口地址,开始运行FSBL固件。 内部ROM首先从选定的Flash设备中读取FSBL镜像文件并运行,但是此时DDR还没有初始化,那么FSBL镜像在哪里运行呢?肯定是内部RAM啊,ST32MP1内部有256KB的SYSRAM,如图5.2.2.1所示: % I. c) p. R8 n4 k' d! g' _2 c+ T& |& { ![]() 图5.2.2.1 STM32MP1内部SYSRAM & S, N+ j; l# p! { 从图5.2.2.1中可以看出,SYSRAM地址范围为:0X2FFC0000~0X2FFFFFFF,一共是256KB。ROM代码会将FSBL镜像拷贝到0X2FFC2400地址,但是要注意,FSBL镜像的起始地址不是0X2FFC2400,因为FSBL镜像前面还有一个256(0X100)字节的头部信息,因此FSBL镜像的真正起始地址为0X2FFC2400+0X100=0X2FFC2500。为什么要讲明这一点呢?因为我们可以将FSBL镜像换成A7裸机例程,我们在编译A7裸机例程的时候要指定链接起始地址,这个链接起始地址就是0X2FFC2500。由此可以计算出整个FSBL镜像大小不能超过0X30000000-0X2FFC2500=252672B=246.75KB,246.75KB足够我们编写裸机例程了。 FSBL镜像鉴权成功以后,ROM代码会boot上下文的起始地址保存到R0寄存器,然后跳转到FSBL镜像的入口地址,这个入口地址会定义到头部里面,其实就是上面讲的0X2FFC2500。 boot上下文我们不需要管,但是还要简单讲一下,boot上下文会保存到SYSRAM的前512字节里面,boot上下文包含了boot信息,比如选定的boot设备,还有一些和安全启动鉴权有关的服务。结构体boot_api_context_t定义了boot上下文结构,boot_api_context_t结构体是定义在TF-A源码里面的(plat/st/stm32mp1/include/boot_api.h),内容如下所示:
boot_api_context_t结构体目前不需要去研究,后面学习TF-A的时候根据实际情况在看是否有必要学习。 5.2.3 串行启动6 m2 g T; X1 w# h- |. |0 }: d 当我们设置BOOT2~BOOT0为串行启动,也就是从USB或UART启动的时候就会进入此模式。当选择串行启动以后ROM代码就会并行扫描所有可以启动的UART以及USB OTG接口。当扫描到某个活动的串行接口以后,ROM代码就会使用此串行接口,并且忽略掉其他的串行接口。 1、USB启动 内部ROM代码支持USB OTG启动,我们一般使用STM32CubeProgrammer软件通过USB OTG接口来向STM32MP1烧写系统。USB OTG需要一个48M和60M的时钟,这两个时钟由HSE生成。ROM代码支持的HSE时钟值如下:3 M3 v1 [ A/ H: U6 L 8, 10, 12, 14, 16, 20, 24, 25, 26, 28, 32, 36, 40, 48 MHz0 M7 s o7 c7 ?0 o$ i) \1 \! ] 正点原子STM32MP157开发板使用24M有源晶振作为HSE时钟源。我们可以通过设置OTP来更改ROM代码的HSE晶振大小,设置如表5.2.3.1所示: OTP WORD 3值 (2bit) 描述 00 默认模式,ROM代码自动检测HSE频率,HSE频率必须为8, 10, 12, 14, 16, 20, 24, 25, 26, 28, 32, 36, 40或 48MHz,如果没有检测到的话就直接认为是24MHz8 f, v- @. I/ X 01 HSE=24MHz 10 HSE=25MHz 11 HSE=26MHz" H) x) p* R/ {! u I* {& Q6 R0 d 表5.2.3.1 HSE时钟OTP设置表; ]8 `/ ^+ x6 t 从表可以看出,默认情况下HSE选择24MHz,虽然可以通过修改OTP来更改HSE,但是强烈不建议!因为OTP只能修改一次,一旦修改错误芯片就废了!所以大家在自己做核心板的时候外部HSE时钟最好选择24MHz,不要特立独行! 2、UART启动 如果要送UART启动,也就是通过UART烧写系统,那么只能使用USART2、USART3 UART4、UART5、USART6、UART7或UART8,此时串口工作模式为:1位起始位、8位数据位、偶校验、1位停止位、波特率115200。 由于STM32的IO复用功能,1个串口可能有多个IO可以使用,比如UART4的RX(接收)可以使用PI10、PH14、PA1、PA11、PB2、PB8、PC11、PD0或PD2,一共9个IO可以用作UART4_RX引脚,但是ROM代码里面的UART4_RX引脚肯定只会使用这个9个里面的其中一个,所以我们板子的串口引脚要和ROM代码里面的一致,否则就无法使用串口启动。ROM代码里面串口使用的引脚如表5.2.3.2所示:: U9 h) O! c8 M7 s# i' b+ D8 L% C" l( W 串口 串口引脚 所使用的IO 复用编号(AF)$ U; D& R2 i6 L$ e USART2 USART2_RX PA3 AF07! p1 q. Z- H( m/ T USART2_TX PA2 AF07 USART3 USART3_RX PB12 AF08: d* L! m: R/ D0 I+ | Z USART3_TX PB10 AF07 UART4 UART4_RX PB2 AF084 b \: {8 s8 `- X" g% T% X UART4_TX PG11 AF06 UART5 UART5_RX PB5 AF12# f1 B7 s4 |/ j. v" R; R8 V$ L( O UART5_TX PB13 AF14 USART6 USART6_RX PC7 AF07 USART6_TX PC6 AF074 ]5 d/ K; B& Y& A/ D/ b UART7 UART7_RX PF6 AF076 k+ A# K; j5 g$ r' S; g% R! a# H9 f3 h UART7_TX PF7 AF07 UART8 UART8_RX PE0 AF08 UART8_TX PE1 AF08 表5.2.3.2 ROM代码串口默认IO$ I: Q' ^7 [ @4 V 大家在做板子的时候,如果要使用串口启动,那么相关串口IO一定要参考表5.2.3.2中定义的IO引脚,比如正点原子开发板UART4的RX引脚使用PB2,TX引脚使用PG11。 5.3 Flash设备启动要求 STM32MP1支持从SD、EMMC、NAND或NOR等Flash设备启动,但是不同的Flash设备在启动的时候有不同的要求。linux系统他不像单片机那样,就一个bin文件,烧写进去就可以启动并运行,linux系统自身编译出来就是一个镜像文件,但是这个镜像文件要运行是需要一大堆的“小弟”来辅助。比如需要uboot来启动,启动以后还需要根文件系统(rootfs),传统的嵌入式linux有三巨头:uboot、kernel和rootfs,但是对于STM32MP1而言,由多了几个“小弟”,比如TF-A、TEE、vendorfs等,所有这一大堆构成了最终的系统镜像。系统镜像是要烧写到Flash设备中的,这些不同的文件肯定要按照一定的要求,分门别类的烧写,一个萝卜一个坑,TF-A应该放到哪里、uboot应该放到哪里等等。 针对Flash设备,可以通过创建不同的分区来存放不同的文件,ST针对STM32MP1系列给出了官方分区建议,这些建议包含了Flash分区数量、分区最小空间、分区存放的内容等,如表5.3.1所示:3 w6 W* B: X8 S3 X 尺寸 分区 描述 256KB~512KB fsbl 第一阶段启动代码,此分区存放TF-A或者uboot的SPL部分,如果写A7裸机例程的话此分区也用来存放裸机代码。% y3 \+ z* W& {) ?5 K 2MB ssbl 第二阶段启动代码,一般是uboot,如果uboot使用设备树的话,设备树添加到后面。+ Z) d+ z c$ S* }4 R. J 64MB bootfs boot文件分区,可以存放如下内容: · init ram文件系统,可以将此文件系统拷贝到RAM中,在linux内核挂载正式根文件系统之前可以使用init ram文件系统。# ~! ~8 B# V$ M( `$ E4 H ·linux内核设备树 ·linux内核7 v7 p* s5 r+ A* p ·uboot显示的启动界面 ·uboot发行配置文件extlinux.conf& ]4 k5 b7 d8 k/ } 16MB vendorfs 此分区存放第三方的版权信息,确保它们不会受到任何开源许可的污染,比如GPL V3。 768MB rootfs linux根文件系统。7 j w- I; q8 y- p 剩余空间 userfs 用户自行使用的剩余空间' p3 |, d4 w# J" _9 e" @6 L; K* q( L 表5.3.1 ST官方的Flash分区建议 5.3.1 从NAND启动& [& m f( S- g! _( h8 z& e# k" _ NAND前几个块(block)里面包含了多份FSBL,ROM代码会从第一个块开始扫描,并且加载第一个有效块里面的FSBL。ROM代码支持并行NAND和串行NAND,并行NAND连接到FMC总线上,串行NAND连接到QSPI上。 ROM代码支持的并行NAND要求如表5.3.1.1所示:2 ?9 w! k6 Q9 C& t/ T0 G/ ` 块大小(KB) 页大小(KB) 数据宽度 ECC(bit数和编码)1 ~9 d1 l) J; Z/ w- A2 ^ _! [ 128 2 8,16 4(bch),8(bch),1(hamming) 256 4 8,16 4(bch),8(bch),1(hamming) 512 4 8,16 4(bch),8(bch),1(hamming) k* R1 j v8 L5 l0 m/ A 512 8 8,16 4(bch),8(bch),1(hamming) 表5.3.1.1 并行NAND参数要求 ROM代码支持串行NAND要去如表5.3.1.2所示: 块大小(KB) 页大小(KB) 128 2 256 47 i- O8 w. w7 q, o- @; U- X 512 48 }1 J: w- C& N 512 83 D9 Z0 `# m' g) T% [6 _ 表5.3.1.2 串行NAND参数要求 所以大家在制作STM32MP1硬件的时候,NAND Flash选型一定要符合表5.3.1.1中的参数。 5.3.2 从EMMC启动 EMMC在物理结构上有boot1、boot2、RPMB(Replay Protected Memory Block)、GPP(General Purpose Partitions,GPP最多4个分区)以及UDA(User Data Area)这5种分区,比如三星的KLM系列EMMC 5.1的分区结构如图5.3.2.1所示: 4 S3 G6 u1 e: s+ \6 k2 _ ![]() 图5.3.2.1 三星KLM系列EMMC分区结构 我们一般知道和常用的就是UDA分区,也就是用户数据区域,很少会关心boot1、boot2这样的分区。boot1、boot2、RPMB这三个分区代销是固定的,用户不能修改,boot1、boot2分区存在的意义就是用于引导系统。正点原子STM32MP157开发板所使用的EMMC型号为KLM8G1GETF,这是三星的一颗8GB EMMC 5.1芯片,boot1、boot2和RPMB分区大小如图5.3.2.2所示: 0 y/ V0 b0 d) r' O! l+ k0 S" P: m% W ![]() 图5.3.2.2 KLM系列EMMC分区 从图5.3.2.2中可以看出,对于三星的8GB的EMMC而言,boot1和boot2分区默认大小为4096KB,RPMB为512KB。 ST会使用EMMC的boot1和boot2这两个分区作为FSBL,但是同一时间只有一个有效,ROM代码会加载有效的哪个FSBL。ROM代码使用单bit模式来操作EMMC,默认情况下ROM代码使用连接到SDMMC2上的EMMC,可以通过OTP来修改EMMC所使用的SDMMC接口,但是这里不建议!7 P; a3 e2 h- k# a5 H' | 5.3.3 从SD卡启动 SD卡也包含两个FSBL,但是SD卡没有boot1和boot2这样的物理分区。ROM代码默认尝试加载第一个FSBL,如果第一个FSBL加载失败,那么ROM代码就会加载第二个FSBL。' A7 _3 R# T' x r7 k9 |% h0 N ROM代码首先在SD卡上查找GPT分区,如果找到的话就查找名字以“fsbl”开始的两个FSBL分区。如果没有找到GPT分区的话就直接根据物理地址查找两个FSBL,第一个FSBL的起始偏移地址为LBA34,地址位34512=17408=0X4400,所以第一个FSBL的起始地址为0X4400。第二个FSBL的起始偏移地址为LBA546,地址为 546512=279552=0X44400,所以第二个FSBL的起始地址为0X44400。2 D& e, N. `% w5 P( z9 n1 [ ROM代码默认也是使用单bit模式操作SD卡,并且默认使用连接到SDMMC1接口上的SD卡。 5.4 STM32MP1二进制头部信息 前面讲了STM32MP1内部的ROM代码会先读取FSBL代码,一般是TF-A或者Uboot的SPL,也可以是A7裸机代码。比如TF-A我们之间编译生成二进制bin文件,但是这个bin文件不能直接拿来用,需要在前面添加一段头部信息,这段头部信息也包含了鉴权内容。加入头部信息以后的FSBL代码结构如图5.4.1所示:( B: C0 \0 `: W' X( z ![]() 图5.4.1 FSBL镜像组织结构0 I+ L+ \* g+ R3 L& ] [6 a1 H 头部信息一共是256字节,这256个字节的头部信息具体含义如表5.4.1所示:# w) M1 ?$ d/ L4 ]: ]# B! ^ 名字 长度 偏移(B) 描述; @/ C2 J7 f. u$ S1 C Magic number 32bits 0 魔术数,4字节,为‘S’、‘T’、‘M’和0X32的组合,固定为0X53544D32。注意,这4个字节是大端模式,也就是高字节存储在低地址处,低字节数据在高地址处。+ v" h" q- h& |) q+ j: T+ W/ k Image signature 512bits 4 ECDSA签名,用于镜像鉴权。! N* ~; K1 j% V" {' @# Z Image checksum 32bits 68 镜像校验和。 Header version 32bits 72 头部版本信息,V1.0的话为0X00010000,含义: Byte0:保留( O5 W6 K% {* g* V( ^( c Byte1:主版本号为0X01。 Byte2:次版本号为0X00。8 v, c; \, U3 X" `: ? Byte3:保留 Image length 32bits 76 镜像长度,不包含头部,单位为字节。 Image entry Point 32bits 80 镜像入口地址。3 r& j: X! T& n$ G4 G- v1 k Reserved1 32bits 84 保留。8 R |" d0 N ~ Load address 32bits 88 镜像加载地址,ROM代码不使用此地址。 Reserved2 32bits 92 保留。 Version number 32bits 96 镜像版本信息。 Option flags 32bits 100 可选字段,b0=1的话表示不需要验证签名。6 t+ f$ q" J4 [0 n' { ECDSA algorithm 32bits 104 ECDSA算法,1:P-256 NIST;2:brainpool 2562 S. l' M! d, H" B. q ECDSA pubilc key 512bits 108 ECDSA公共秘钥,签名的时候使用。 Padding 83Bytes 172 保留的填充区域,必须全部为0 Binary type 1Byte 255 二进制文件类型:' a+ D" b1 U) b# r! a 0X00:U-Boot4 q1 T, m; t2 g/ S: x. D$ L( { 0X10-0X1F:TF-A 0X20-0X2F:OPTEE 0X30:Copro 表5.4.1 头部信息含义0 V, Y0 y% n- O0 G0 N+ ` 头部信息不需要我们自己手动添加,我们在编译ST官方提供的TF-A或者Uboot的时候会自动添加,因为ST提供了个名为“stm32image”的工具专门用于在bin文件前面添加头部信息。我们已经从TF-A源码中提取出来了stm32image并放到了开发板光盘中,路径为:开发板光盘5、开发工具2、ST官方开发工具stm32imagestm32image.c。在编写A7裸机的时候需要自己使用stm32image工具在bin文件前面添加头部信息,stm32image是在Ubuntu下运行的,所以需要先编译,将stm32image.c发送到Ubuntu下,然后输入如下命令编译:. G2 |3 J4 q. v8 Y gcc stm32image.c -o stm32image 编译成功以后就会生成一个名为stm32image的可执行文件,如不5.4.2所示:- T, K% m m) A$ g% D6 s " x% G# y8 Y v% E7 u ![]() 图5.4.2 stm32image工具; a5 T% V2 d# c8 v' r! l7 |# Z# A 运行图5.4.2编译出来的stm32image工具,输入“-s”选项可以查看使用方法,如图5.4.3所示:! _; ] t, H* K Z) ?3 w4 O4 G ![]() 图5.4.3 stm32image使用方法5 N! r- p# E" p* ` 从图5.4.3可以看出,stm32image在使用的时候需要搭配一系列的参数:' S* k# p( U# j: k/ j: U, H -s:指定源文件。/ T! g& j4 e! ~0 M$ F" r -d:生成的目标文件。3 H: c' I- B7 r- R7 ^/ s: j [ -l:加载地址。& h% ^! U! g3 b) ? -e:入口地址。 -m:出版本号。) w" [) B7 @. o1 w -n:次版本号。 大家在开发板光盘里面找到正点原子出厂的tf-a固件,路径为:开发板光盘8、系统镜像2、出厂系统镜像1、STM32CubeProg烧录固件包tf-atf-a-stm32mp157d-atk-trusted.stm32,这个就是加入了头部信息的TF-A可执行文件。使用winhex软件打开tf-a-stm32mp157d-atk-trusted.stm32,winhex软件已经放到了开发板光盘中,路径为:开发板光盘 3、软件 winhexv19.7.zip,大家自行安装即可。安装完成以后打开winhex,然后点击:文件->打开,找到tf-a-stm32mp157d-atk-trusted.stm32并打开,如图5.4.4所示: . G+ u6 P! `$ ~. O4 B$ [ ![]() 9 Y ]; u' W0 k. Y9 o 图5.4.4 tf-a-stm32mp157d-atk-trusted.stm32文件内容 图5.4.4就是tf-a-stm32mp157d-atk-trusted.stm32文件原始数据,其中前256个字节就是头部信息。这里我们根据图5.4.4中的内容,分析一下tf-a头部信息中几个比较重要的参数: Magic number:起始偏移地址为0,长度为4个字节,值依次为:0X53、0X54、0X4D、0X32,合起来就是0X53544D32,这个就是表5.4.1中的魔术数,注意这四个字节的顺序是大端模式。 O+ m% b4 D) Z: N4 ]3 |0 o" b Header Version:起始偏移地址为72,长度为4个字节,也就是图5.4.4中第72~75这4个字节的数据,分别为:0X00、0X00、0X01和0X00,此时有的朋友将这四个字节拼起来发现是0X00000100,发现并不是表5.1.1中的0X00010000!这不是弄错了?肯定不是的,整个头部信息中,除了Magic number采用大端模式存储以外,其他都是小端模式存储,也就是低字节数据存放在底地址处,高字节数据存放在高字节处。因此0X00、0X00、0X01和0X00这四个字节的数据正确的拼出结果为0X00010000。 Image length:起始偏移地址为76,长度为4个字节,也就是图76~79这4个字节的数据,为:0X40、0XB0、0X03和0X00,按照小端模式拼起来就是0X0003B040=241728≈236.1KB,说明此TF-A的bin镜像大小为236.1KB。 Image entry Point:起始偏移地址为80,长度为4个字节,也就是图80~83这4个字节的数据,为:0X00、0X60、0XFD和0X2F,按照小端模式拼起来就是0X2FFD6000,说明入口地址为0X2FFD6000。 Load address:起始偏移地址为88,长度为4个字节,也就是图88~91这4个字节的数据,为:0X00、0X25、0XFC和0X2F,按照小端模式拼起来就是0X2FFC2500,说明加载地址为0X2FFC2500,这个不正是我们前面在5.2.2小节中分析的FSBL镜像起始地址。4 N9 W! b. T9 }# c2 L: o Binary type:起始偏移地址为255,也就是最后一个字节,为0X10,表示当前二进制文件是TF-A。: e) x/ U4 }5 S6 ` h 5.5 STM23MP1 Linux系统启动过程 前面已经对STM32MP1的启动流程做了详细的讲解,STM32MP1是面向Linux领域的,因此所以的这些启动过程都是为了启动Linux内核。STM32MP1xil启动Linux内核的流程如图5.5.1所示:# m' W b% Y: {, v) y ![]() 图5.5.1 MP1 Linux启动流程 从图5.5.1可以看出,STM32MP1启动linux内核一共分为5个步骤,我们依次来看一下这五个步骤的内容:0 G& X- E: b% }/ w. a' a ①、ROM代码 前面说了很多次了,这是ST自己编写的代码,在STM32MP1出厂的时候就已经烧写进去的,不能被修改的。ROM代码因为保存在STM32内部ROM里面,因此也就直接简单明了的叫做“ROM代码”了。它是处理器上电以后首先执行的程序,ROM代码的主要工作就是读取STM32MP1的BOOT引脚电平,然后根据电平判断当前启动设备,最后从选定的启动设备里面读取FSBL代码,并将FSBL代码放到对应的RAM空间。8 y, v1 O, Z! Q, X; w 现在很多产品对设备上运行的应用都提出了安全要求,从图5.5.1中可以看出,STM32MP1启动Linux内核的过程是一个链式结构:ROM CodeFSBLSSBLLinux kernelrootfs,系统启动的过程中要保证整个链式结构都是安全的。ROM代码作为第一链,首先要对FSBL代码进行鉴权,同样的,FSBL以及后面的每一链都要对下一个阶段的镜像进行鉴权,直到设备系统正确启动。' S) a* i2 W2 g ②、FSBL FSBL代码初始化时钟树、初始化外部RAM控制器,也就是DDR。最终FSBL将SSBL加载到DDR里面并运行SSBL代码。 一般FSBL代码是TF-A或者Uboot的SPL代码,前面我们说了,也可以将FSBL换成我们自己编写的STM32MP1 A7内核裸机代码。 ③、SSBL 由于SSBL代码运行在DDR里面,无需担心空间不够,因此SSBL代码的功能就可以做的很全面,比如使能USB、网络、显示等等。这样我们就可以在SSBL中灵活的加载linux内核,比如从Flash设备上读取,或者通过网络下载下载等,用户使用起来也非常的友好。SSBL一般是Uboot,用来启动Linux内核。2 l+ D; E% l4 i4 p; s5 \4 f ③、Linux内核 SSBL部分的Uboot就一个使命,启动Linux内核,Uboot会将Linux内核加载到DDR上并运行。Linux内核启动过程中会初始化板子上的各种外设。2 j+ F1 }+ [0 h1 Z2 W9 A2 u ④、Linux用户空间% o$ a+ ?/ Y+ @4 u& F( T7 n, l 系统启动的时候会通过init进程切换到用户空间,在这个过程中会初始化根文件系统里面的各种框架以及服务。 ————————————————7 f' y# h$ `( C 版权声明:正点原子 2 G1 }8 d" m% t 7 v2 f# z9 h, J |
基于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安全启动》 课程学习心得