• 正点原子嵌入式linux驱动开发——TF-A移植


    经过了之前的学习,除了TF-A的详细启动流程仍待更新,TF-A的使用和其对应的大致启动流程已经进行过了学习。但是当我们实际做产品时,硬件平台肯定会和ST官方的有区别,比如DDR容量会改变,自己的硬件没有使用到官方EVK开发板所使用的PMIC芯片等等。因此就涉及到将半导体原厂提供的TF-A移植到我们的硬件上,本章就来讲解一下如何将ST官方提供的TF-A源码移植到正点原子的STM23MP157开发板上

    编译ST官方TF-A源码

    编译官方TF-A源码的原因

    所谓的移植就是让半导体官方提供的软件在自己的硬件平台上运行起来,准确的说应该是将自己的硬件添加到官方软件包TF-A是ARM官方出品的一个软件包,半导体厂商会从ARM官方下载这个最正宗的TF-A软件包,然后将自己公司的SOC芯片添加进去,最终打包好提供给SOC用户,这个就是所谓的SDK包。我们在之前完成的对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源码,目的如下:

    1. 掌握半导体官方软件编译方法
    2. 验证开发环境搭建是否正确,比如交叉编译器设置是否正确,所依赖的第三方库有没有安装等;
    3. 观察编译结果,比如编译完成以后的可执行文件保存在哪个目录下,都有哪些可执行文件及其区别等;
    4. 在自己的开发板上运行编译出来的可执行文件,所谓的移植就是改bug的过程,将编译出来的STM32MP157官方开发板可执行文件在自己的板子上运行,然后观察运行过程有没有错误,没错误最好,有错误就改,直到能在自己的开发板上正常运行,这就是移植过程。

    编译TF-A

    在之前的学习中已经准备好了ST官方的TF-A源码(按照之前的步骤完成打补丁之后再编译),可以另外新建一个名为“my-tfa”的目录来存放并编译ST官方的TF-A源码,如下图所示:
    TF-A源码文件
    上图中我们重点关注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源码文件,也就是我们要编译的。

    修改Makefile.sdk

    首先要修改上图中的Makefile.sdk,因为此文件定义编译选项,包括交叉编译器,要使用自己的交叉编译器,也就是已经安装的arm-none-linux-gnueabihf-gcc编译器。用VIM编辑器打开Makefile.sdk找到“CROSS_COMPILE”,将其改为“arm-none-linux-gnueabihf-”;修改完成后保存退出,进入到tf-a-stm32mp-2.2.r1目录下,然后进行编译,命令如下:

    cd tf-a-stm32mp-2.2.r1/ //进入到 tf-a源码目录下
    make -f ../Makefile.sdk all //编译

    如果需要加快编译速度,可是使用多线程编译,线程数 最好和自己给虚拟机分配的物理核心一致,使用-j来指定线程数,命令如下:

    make -f ../Makefile.sdk -j8 all //使用 8线程编译

    编译完成以后会在tf-a-stm32mp-2.2.r1文件夹的上层目录 (也就是Makefile.sdk同级目录下)下生成一个名为“build”的文件夹,build文件夹下就是编译生成的所有可执行文件,进入到build目录下,有三个子目录,如下图所示:
    编译结果
    上图中有3个子目录:optee、serialboot和trusted,说明有3个版本的可执行文件,optee是针对 OP-TEE的,本教程没使用OP-TEE,所以optee目录和我们没关系。

    serialboot看名字就知道是和串行启动有关的,也就是通过串口或者USB烧写系统的时候需要用到serialboot下的可执行文件,之前编写STM32CubeProgrammer所使用的Flashlayout文件(.tsv)的时候用到了一个名为“tf-a-stm32mp157d-atk-serialboot.stm32”的文件,此文件就是 serialboot目录下的。当然,上图中目前还没有“tf-a-stm32mp157d-atk-serialboot.stm32”这个文件。当把TF-A移植到自己开发板上以后就会在serialboot目录下生成我们板子对应的xxx-serialboot.stm32文件

    trusted目录下的就是烧写到开发板中的TF-A镜像,进入 trusted目录,如下图所示:
    TF-A镜像文件
    从上图可以看出,编译出来了很多后缀为.bin、.elf、.ld、.stm32这样的文件, 以
    前缀为“tf-a-stm32mp157d-ev1”的文件为例,相关的文件如下图所示:
    使用STM32MP157D芯片的EV1开发板对应文件
    从上图可以看出,这些文件都是针对使用STM32MP157D芯片的EV1开发板,同理,
    其他的文件命名也是类似的,我们只需要选择和我们所使用开发板接近的文件就行了。比如正点原子 STM32MP157开发板使用的主控型号是STM32MP157DAA1,开发板硬件参考了ST官
    方的STM32MP157 EVK开发板
    ,因此我们就需要使用上图中的这些文件,在移植的时候需要参考的对象就是stm32mp157d-ev1

    另外,上图中的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文件夹下。

    烧写ST官方TF-A镜像

    将编译出来的tf-a-stm32mp157d-ev1-trusted.stm32烧写到正点原子开发板EMMC里面,烧写方法在之前的笔记中有所讲解。烧写完成以后打开串口,设置好串口调试软件MobaXterm,设置开发板从EMMC启动,按下复位按键,观察MobaXterm软件接收到的log信息,如下图所示:
    运行结果
    上图代表着就是运行崩溃了,说明ST官方开发板对应的TF-A不能直接在正点原子的开发板上运行,需要进行修改。

    TF-A移植

    经过了上述的编译测试,ST官方的TF-A镜像并不能直接在正点原子的开发板上运行,所以需要进行移植操作。

    注意,TF-A的移植全部是修改设备树,初学linux(这个文档在针对我,我就是初学者)肯定对设备树 (device tree)不了解,而设备树是一个系统的概念,会在后面的 linux驱动开发里面详解设备树的相关语法!所以初学的朋友直接根据接下来的教程修改就行了,不用深入研究其原理。或者,直接跳过TF-A的移植,直接使用正点原子移植好的。

    新建开发板对应的设备树

    创建板子对应设备树

    设备树英文名字叫做Device tree,用来描述板子硬件信息,比如板子上的CPU有几个核、每个CPU核主频是多少,IIC、SPI这些外设的寄存器范围是多少,IIC接口下都挂了哪些设备等等。设备树文件是一种文本格式的文件,方便阅读与修改,文件后缀为.dts,设备树也有头文件,头文件后缀为.dtsi。和C语言一样,.dts可以引用头文件.dtsi,.dts经过编译以后生成.dtb文件,.dtb就是开发板要使用的。类似于我们将C语言的.c文件编译为.bin文件,然后在开发板上运行,将.dts编译为dtb的工具叫做DTC,并不是我们使用的gcc编译器

    打开tf-a-stm32mp-2.2.r1\fdts目录,fdts目录下保存的就是所有开发板的设备树文件,其中就包括了STM32MP1系列的,如下图所示:
    TF-A设备树文件
    上图中的stm32mp157d-ev1.dts就是STM32MP157D EVK开发板所对应的设备树文件,同理还有STM32MP157其他型号的芯片,比如stm32mp157a、stm32mp157c等。我们以stm32mp157d-ev1.dts为蓝本,添加正点原子STM32MP157开发板对应的设备树文件

    创建VScode工程,然后用VScode打开,打开stm32mp157d-ev1.dts文件,文件内容下图所示:
    stm32mp157d-ev1.dts文件内容
    上图中第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 fdts
    cp stm32mp157d-ed1.dts stm32mp157d-atk.dts //复制

    复制完成以后打开stm32mp157d-atk.dts文件,内容如下图所示:
    stm32mp157d-atk.dts文件内容
    第8-13行,头文件引用,其中第8-11行引用STM32MP15X芯片相关的dtsi头文件,这些全部保留即可

    第12行,非常重要,“stm32mp15xx-edx.dtsi”,是edx系列开发板的通用头文件,适合具体板子有关的,很明显,板子不同其对应的板子头文件也不同。我们同样需要以stm32mp15xx-edx.dtsi为蓝本,创建正点原子开发板对应的板子头文件,一会再创建

    第20行,stdout-path表示标准输出,也就是设置TF-A信息输出路径,这里设置为serial0,也就是串行接口0(注意,不是STM32MP157的串口 0),波特率为115200;

    第24行,设置serial0对应STM32MP157的串口4,所以 TF-A会使用STM32MP157的串口4作为信息输出接口,在做板子的时候最好选择串口4作为通信接口

    最后,需要以stm32mp15xx-edx.dtsi为蓝本,制作正点原子开发板对应的头文件,方法很简单,直接复制一份stm32mp15xx-edx.dtsi,然后将其重命名为“stm32mp157d-atk.dtsi”,命令如下:

    cd fdts
    cp stm32mp15xx-edx.dtsi stm32mp157d-atk.dtsi

    拷贝完成以后就需要修改stm32mp157d-atk.dts,将第12行改为

    #include "stm32mp157d-atk.dtsi"

    修改以后的stm32mp157d-atk.dts文件内容如下(有缩减):
    stm32mp157d-atk.dts文件内容

    编译

    设备树创建好以后编译一下,在编译之前要先修改以后Makefile.sdk,因此我们前面新建的stm32mp157d-atk.dts并没有添加到编译列表里面,直接编译的话并不会编译此文件。打开Makefile.sdk,在TFA_DEVICETREE配置项中添加“stm32mp157d-atk”,如下图所示:
    修改后的TFA_DEVICETREE
    修改完成以后保存退出,然后执行如下命令编译一下TF-A:

    cd tf-a-stm32mp-2.2.r1/ //进入到 tf-a源码目录下
    make -f ../Makefile.sdk all //编译

    如果修改没有错误的话就会编译成功,编译完成以后进入build/trusted目录下,此时就会生成以“tf-a-stm32mp157d-atk”开头的相关文件,如下图所示:
    编译生成的正点原子开发板TF-A固件
    上图就是烧写的TF-A固件,其设备树(dtb)已经替换为上面新建的stm32mp157d-atk.dts了。但是由于并没有对TF-A源码做什么修改,所以tf-a-stm32mp157d-atk-trusted.stm32依旧是不可运行的,错误类型和之前的一样,可以自行烧写测试。

    修改设备树电源管理

    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芯片性能非常强大:

    • 输入电压 2.8V~5.5V;
    • 4路可调的通用LDO输出;
    • 1路 DDR3端接LDO电源;
    • 1路 USB PHY所使用的LDO电源;
    • 1路 DDR参考电压LDO电源;
    • 4路可调的BUCK开关电源;
    • 1路 5.2V/1.1A的BOOST开关电源;
    • 1路 500mA的USB OTG电源;
    • 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文件,找到如下所以代码:
    在这里插入图片描述
    第45行,“i2c4”表示这段代码是和I2C4控制器有关的,在设备树中一个IIC接口下的所有设备都“打包”放到一个节点下,在这里就是描述 I2C4这个 IIC控制器下的所有 IIC器件。ST官方的STM32MP157开发板将STPMIC1A这个PMIC芯片挂到了I2C4下,所以STPMIC这个芯片的相关信息就会在“i2c4”这个节点下进行描述;

    第54~281行,这一大段为STPMIC1A这颗芯片的描述信息,第54行是“pmic: stpmic@33”,即使我们没有学习过设备树,但是也能看出这是和PMIC有关的。在这一大段代码中,描述了STPMIC1A各输出电源应该如何配置,比如电压应该是多少,每路电源的功能是什么等

    将上图中第54~281行的内容全部删除掉,也就是删除掉STPMIC1A芯片的相关描述,删除以后的 i2c4节点内容如下图所示:
    删除pmic后的i2c4节点
    由于我们上面把PMIC相关的信息都删除了,但是其他设备的电源信息还是要有的,只是不通过 PMIC来描述了。所以需要我们自己添加一些设备的电源描述信息,在stm32mp157d-atk.dtsi文件里面找到“vin”节点,如下图所示:
    vin节点内容
    将上图中的vin节点内容全部删除掉,也就是将 17~23行代码删除掉,替换为下面的代码:

    vddcore: regulator-vddcore {
    	compatible = "regulator-fixed";
    	regulator-name = "vddcore";
    	regulator-min-microvolt = <1200000>;
    	regulator-max-microvolt = <1350000>;
    	regulator-off-in-suspend;
    	regulator-always-on;
    };
    
    v3v3: regulator-3p3v {
    	compatible = "regulator-fixed";
    	regulator-name = "v3v3";
    	regulator-min-microvolt = <3300000>;
    	regulator-max-microvolt = <3300000>;
    	regulator-off-in-suspend;
    	regulator-always-on;
    };
    
    vdd: regulator-vdd {
    	compatible = "regulator-fixed";
    	regulator-name = "vdd";
    	regulator-min-microvolt = <3300000>;
    	regulator-max-microvolt = <3300000>;
    	regulator-off-in-suspend;
    	regulator-always-on;
    };
    
    vdd_usb: regulator-vdd-usb {
    	compatible = "regulator-fixed";
    	regulator-name = "vdd_usb";
    	regulator-min-microvolt = <3300000>;
    	regulator-max-microvolt = <3300000>;
    	regulator-off-in-suspend;
    	regulator-always-on;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    第17-24行,描述VDDCORE电源,也就是STM32MP157的内核电源,最小为1.2V,最
    大为1.35V;

    第26-33行,描述3.3V电源,最小和最大都是3.3V;

    第35-42行,描述VDD电源,这是一个3.3V的电源,所以最小和最大都为3.3V;

    第44-51行,描述VDD_USB电源,为3.3V,所以最小和最大都为3.3V。

    修改完后如下图所示:
    修改后电源信息
    至此,电源管理修改完成了,不要编译,会报错。因为修改了电源设置,而USB OTG节点还是引用的以前相关电源设置,所以编译会报错,等修改好USB OTG设备节点以后就可以编译了

    修改TF卡和EMMC设备树

    继续操作stm32mp157d-atk.dtsi这个文件,找到“sdmmc1”和“sdmmc2”这2个节点,节点内容如下图所示:
    sdmmc1和sdmmc2原节点内容
    将上图中的sdmmc1和sdmmc2节点改为如下所示内容:

    &sdmmc1 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>;
    	st,neg-edge;
    	broken-cd;
    	bus-width = <4>;
    	vmmc-supply = <&v3v3>;
    	status = "okay";
    };
    
    &sdmmc2 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>;
    	non-removable;
    	st,neg-edge;
    	bus-width = <8>;
    	vmmc-supply = <&v3v3>;
    	vqmmc-supply = <&v3v3>;
    	status = "okay";
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    修改完成以后sdmmc1和sdmmc2如下图所示:
    修改后的sdmmc1和sdmmc2节点
    同样,不要编译,会报错,因为修改了电源,而USB OTG节点还是引用的以前相关电源设置,所以编译会报错,等修改好USB OTG设备节点以后就可以编译了。

    修改USB OTG设备树

    最后就是修改一下USB OTG对应的设备树节点,继续操作stm32mp157d-atk.dtsi,找到“usbotg_hs”这个节点,节点默认内容如下图所示:
    usbotg_hs原内容
    将上图中的usbotg_hs节点改为如下所示内容:

    &usbotg_hs {
    	phys = <&usbphyc_port1 0>;
    	phy-names = "usb2-phy";
    	usb-role-switch;
    	status = "okay";
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    最后还需要修改“usbphyc”节点里面的“status”属性值,看起来就是向stm32mp157d-atk.dtsi文件里面添加了一个名为“usbphyc”的节点,内容如下:

    &usbphyc {
    	status = "okay";
    };
    
    • 1
    • 2
    • 3

    修改完成以后的usbotg_hs和usbphyc这两个节点如下图所示:
    修改后的usbotg_hs和usbphyc

    编译测试

    编译

    设备树修改完成以后先编译一下TF-A,但是我们编译的时候默认会编译很多STM32MP1开发板的TF-A镜像,我们可以修改Makefile.sdk固件,只编译正点原子开发板对应的TF-A镜像。打开Makefile.sdk文件,将TFA_DEVICETREE修改为如下图所示内容:
    修改后的TFA_DEVICETREE
    上图中的TFA_DEVICETREE用于指定需要编译的板子对应的设备树,这里改为之编译stm32mp157d-atk,也就是正点原子开发板对应的设备树,也就是前面我们自己创建的stm32mp157d-atk.dts文件。

    修改好以后重新编译TF-A,如下图所示,表示编译成功:
    编译成功
    上图中提示tf-a-stm32mp157d-atk.bin编译成功,而且也生成了tf-a-stm32mp157d-atk.stm32镜像文件。

    烧写测试

    使用STM32CubeProgrammer软件将上面编译出来 build/trusted/tf-a-stm32mp157d-atk.stm32烧写到开发板上的EMMC中并运行,运行log信息如下图所示:
    TF-A运行log信息
    从上图中可以看出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)

    这个时候uboot可能会启动,也可能不会启动,这里先不要管uboot能不能启动。

    另外,由于正点原子的STM32MP157开发板出厂已经烧写系统,所以bl2可以加载uboot到DDR上,但是如果用的是全新的核心板,EMMC还没有烧写过任何系统,或者你把EMMC格式化过,因为 EMMC里面没有uboot镜像,所以bl2在加载uboot的时候就会失败,TF-A运行的log信息如下图所示:
    TF-A运行错误
    从上图中可以看出,TF-A提示没有找到ssbl分区 (ssbl分区用于存放uboot),因为是全新的 EMMC核心板,而烧写的时候也没有在FlashLayout中添加ssbl分区信息,所以会提示找不到ssbl分区,从而导致TF-A启动出问题。解决方法很简单,修改FlashLayout,然后在里面添加一个ssbl分区,但是ssbl分区不烧写任何文件,就相当于新建一个名为“ssbl”的分区打开之前已经创建的FlashLayout文件tf-a.tsv,在最后一行添加如下内容:
    添加的ssbl信息
    上面一行的“PE”表示不更新ssbl分区,但如果不存在ssbl分区就会进行创建ssbl分区。其实就是新建一个空的ssbl,添加完成后的tf-a.tsv如下图所示:
    修改后的tf-a.tsv内容
    上图中最后一行就是ssbl分区,**注意0x00080000后面的箭头表示TAB键,这个TAB键是必须要的!**因为最后面还有Binary列的内容,这里我们并没有给出 Binary列的镜像文件名,表示不烧写任何文件,但是位置得留出来,否则烧写的时候STM32CubeProgrammer会 tf-a.tsv语法错误。

    修改完成以后使用新的tf-a.tsv来烧写TF-A镜像,烧写完成以后TF-A运行log信息如下图所示:
    TF-A运行结果
    从上图可以看出,此时没有再提示ssbl分区错误了,但是有另外一个错误,那就是在加载ID=5的镜像的时候加载错误,这个就是uboot镜像。因为全新的EMMC并没有uboot镜像,因此肯定加载不到,除非把uboot镜像也烧写进去,也就是烧写到上面的ssbl分区里面。

    修改tf-a.tsv文件,在ssbl分区添加要烧写的uboot镜像即可,这里直接烧写正点原子官方出厂系统的uboot镜像即可,也就是u-boot.stm32,这个文件已经放到了我们烧写目录里面,修改后的tf-a.tsv文件如下图所示:
    修改后的tf-a.tsv内容
    上图中最后一行的Opt要改为‘P’,也就是更新ssbl分区中的文件,u-boot.stm32要和tf-a.tsv放在同一个目录下。修改完成以后使用新的tf-a.tsv烧写TF-A和uboot镜像到开发板的EMMC里面,烧写完成以后运行TF-A,运行过程的log信息如下图所示:
    TF-A运行信息
    从上图可以看出,TF-A运行正常,而且因为我们也烧写了uboot镜像,因此uboot也启动了,说明TF-A运行正常。

    xxx-serialboot.stm32测试

    在之前的TF-A初探中构建的系统烧写目录中,用到了正点原子出厂镜像中的stm32mp157d-atk-serialboot.stm32,前面讲了STM32CubeProgrammer首先通过USB或串口向开发板下载stm32mp157d-atk-serialboot.stm32,名字里面有个“serialboot”,翻译过来就是串行启动,此镜像主要用于初始化DDR,并且提供USB或串口功能,目的是为了进一步将uboot镜像下载到DDR的指定位置,最终通过uboot来向外部Flash设备烧写整个系统镜像

    所以需要掌握自行编译stm32mp157d-atk-serialboot.stm32的方法,操作很简单。首先打开 Makefile.sdk,然后将EXTRA_OEMAKE_SERIAL改为如下内容

    EXTRA_OEMAKE_SERIAL=$(filter-out STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_SPI_NOR=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1,$(EXTRA_OEMAKE)) STM32MP_UART_PROGRAMMER=1 STM32MP_USB_PROGRAMMER=1
    
    • 1

    修改完成后如下图所示:
    修改后的EXTRA_OEMAKE_SERIAL配置
    输入如下命令编译:

    cd tf-a-stm32mp-2.2.r1/ //进入到 tf-a源码目录下
    make -f ../Makefile.sdk clean //清除以前的编译
    make -f ../Makefile.sdk TFA_DEVICETREE=stm32mp157d-atk TF_A_CONFIG=serialboot ELF_DEBUG_ENABLE='1' all //编译

    编译完成以后就会在…/build/serialboot目录下生成tf-a-stm32mp157d-atk-serialboot.stm32,如下图所示:
    编译生成的tf-a-stm32mp157d-atk-serialboot.stm32
    用上图中的tf-a-stm32mp157d-atk-serialboot.stm32替换我们以前使用的正点原子出厂系统中的文件,替换完成以后的烧写目录如下图所示:
    images目录
    此时上图中的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是否能够正常启动。

    注意!按照本小节讲解的方法编译生成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文件!

    至此,TF-A移植全部完成!!!

    总结

    TF-A的移植流程

    整个TF-A的移植流程如下:

    • 正点原子的开发光盘中有ST官方TF-A的源码,当然可以官网下载,咱们就直接把他拖过来用就行,就是en.SOURCES-stm32mp1-openstlinux-5-4-dunfell-mp1-20-06-24.tar.xz;
    • 使用tar -xvf解压缩后,进入tf-a-stm32mp-2.2.r1-r0,继续tar -xvf解压缩tf-a-stm32mp-2.2.r1-r0.tar.gz,进入解压后的tf-a-stm32mp-2.2.r1之后,通过如下命令打补丁
    for p in `ls -1 ../*.patch`; do patch -p1 < $p; done //打补丁
    • 安装stm32wrapper4dgb,直接拖到Ubuntu之后unzip再make,然后复制到/usr/bin之中就可以了,复制命令如下:
    sudo cp stm32wrapper4dbg /usr/bin
    • 在Makefile.sdk,将交叉编译器改为我们目前所使用的,找到CROSS_COMPILE,将其改为arm-none-linux-gnueabihf-
    • 进入tf-a-stm32mp-2.2.r1之后make
    make -f ../Makefile.sdk -j8 all //使用 8线程编译
    • 打开tf-a-stm32mp-2.2.r1\fdts目录,复制stm32mp157d-ed1.dts并命名为stm32mp157d-atk.dts
    • 复制stm32mp15xx-edx.dtsi并命名为stm32mp157d-atk.dtsi,后在stm32mp157d-ed1.dts中12行改为
    #include "stm32mp157d-atk.dtsi"
    • 正点原子MP157开发板没有PMIC这个电源管理芯片,所以按照教程完成电源的一系列配置,其操作均在stm32mp157d-atk.dtsi文件完成;
    • 配置TF卡和EMMC以及USB OTG设备树;
    • tf-a.tsv中添加uboot.stm32,添加代码如下:
    P 0x06 ssbl Binary mmc1 0x00080000 u-boot.stm32
    • 自行编译stm32mp157d-atk-serialboot.stm32的方法,在Makefile.sdk中添加:
    EXTRA_OEMAKE_SERIAL=$(filter-out STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_SPI_NOR=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1,$(EXTRA_OEMAKE)) STM32MP_UART_PROGRAMMER=1 STM32MP_USB_PROGRAMMER=1
    • 编译serial,命令如下:
    make -f ../Makefile.sdk clean //清除以前的编译
    make -f ../Makefile.sdk TFA_DEVICETREE=stm32mp157d-atk TF_A_CONFIG=serialboot ELF_DEBUG_ENABLE='1' all //编译
    • 以上编译的三个文件:tf-a-stm32mp157d-atk-serialboot.stm32,u-boot.stm32,tf-a-stm32mp157d-atk-trusted.stm32就此完成,放在同一个文件夹中,并创建一个FlashLayout的.tsv文件完成引导,内容如下:
    #Opt Id Name Type Device Offset Binary
    - 0x01 fsbl1-boot Binary none 0x0 tf-a/tf-a-stm32mp157d-atk-serialboot.stm32
    - 0x03 ssbl-boot Binary none 0x0 uboot/u-boot.stm32
    P 0x04 fsbl1 Binary mmc1 boot1 tf-a/tf-a-stm32mp157d-atk-trusted.stm32
    P 0x05 fsbl2 Binary mmc1 boot2 tf-a/tf-a-stm32mp157d-atk-trusted.stm32
    P 0x06 ssbl Binary mmc1 0x00080000 u-boot.stm32
    • 最后完成开发板接线连接PC,然后通过STM32CubeProgrammer完成烧写。

    原理

    TF-A在正点原子另一个国产的I.MX6ULL就是没有的,属于是ST公司处于产品的完整性来确保安全加进来的一个ARM可信固件

    按照TF-A的原理和源码查看,可以分为bl1、bl2、bl31、bl32以及bl33五个部分,但是在STM32MP1系列芯片中,bl1在ROM内部代码中实现,bl31不是AArch32的,bl33就是uboot,所以其实只有两个部分
    STM32MP1 TF-A启动流程

    下一步

    终于就进入了linux驱动开发的传统学习的uboot内容啦!

  • 相关阅读:
    dev-server是怎么跑起来?
    如何获取GC(垃圾回收器)的STW(暂停)时间?
    CentOS Stream9更改ip地址,网关(设置静态ip)
    我们是否生活在一个超大型生物的大脑之中?——对多元宇宙观与生命存在形式的哲学探讨
    基于Lumisoft.NET组件,使用IMAP协议收取邮件
    进程间通信学习笔记(有名管道和无名管道)
    插件器件小的引脚孔,制造过程中可能存在的一些问题
    Go语言-九阴真经
    [暑假]Vue生命周期-笔记
    Mac的Vim配置
  • 原文地址:https://blog.csdn.net/xhj12138/article/details/133492873