[Linux]从零开始的STM32MP157 Linux内核移植

一、前言

        在之前的教程中我们为我们的开发板移植了TF-A,以及Uboot并且我们也学习了Uboot的基本使用,随后了解到了如何使用Uboot拉起Linux系统。我们使用STM32MP157芯片就是为了LInux开发,前面做的所有准备都是为了能够让我们的开发板运行LInux内核,那么现在,终于等到了这一步,本次教程,就来教大家如何为STM32MP157开发板移植一个Linux内核吧!本次教程我们还是采用正点原子的开发板,这里还是建议大家的开发板和我的一样。如果你准备好了,就让我们开始吧!

二、谁适合本次教程

        在看本次教程之前,需要大家已经移植好一个Uboot并且了解如何使用Uboot从网络启动Linux内核,看过之前教程的小伙伴应该知道,我们的Linux内核是必须使用Uboot拉起的。如果你还没有移植好Uboot可以看下面的文章:

STM32MP157Uboot移植:[Linux]从零开始的STM32MP157 U-Boot移植_stm32mp157 uboot-CSDN博客

如果你不会使用Uboot的基础命令或者不知道如何验证自己的Uboot是否移植完整,可以看下面的文章:

STM32MP157Uboot基础命令讲解及功能验证:[Linux]从零开始的STM32MP157 U-Boot命令讲解及功能验证_stm32mp157 uboot-CSDN博客

如果你不知道如何配置Uboot的网络以及如何从网络启动Linux内核,可以看下面的文章:

STM32MP157网络配置及从网络启动:[Linux]从零开始的STM32MP157 U-Boot网络命令讲解及相关配置_vm下载mp157-CSDN博客

在本次教程中我们可能会使用到图形化配置Linux内核,配置原理与图形化工具和Uboot的差不多,如果你对图形化配置以及.kconfig文件不熟可以看下面的文章:

Uboot图形化配置及Kconfig文件讲解:[Linux]从零开始的STM32MP157 U-Boot图形化配置及Kconfig文件讲解_kconfig图形化配置应用于应用层软件代码项目中-CSDN博客

当你已经对上面的内容比较熟悉以后,我们就可以开始为STM32MP157移植Linux内核了。

三、资料的准备

        同样的,本次并没有为大家单独准备资料,这里我们同样使用正点原子的官方资料文章,这里我们会用到“【正点原子】STM32MP1嵌入式Linux驱动开发指南V2.1.pdf”文档。它被放在了正点原子资料文件夹下的“09、文档教程(非常重要)” 目录下:

这里正点原子的官方资料大家应该都知道去哪儿下载,这里就不放链接了。

四、Linux内核移植

        当我们准备好上面的内容以后,我们就可以开始移植Linux内核了。这里我们同样的会先去编译正点原子的Linux内核并且启动,为大家展示大致的网络启动流程,随后会根据ST官方的Linux内核去移植一个我们自己的Linux内核。让我们现在开始吧!

1.编译正点原子的Linux内核        

        这里我们首先需要安装一下编译所需的环境,不管你以前有没有安装过这些库现在都需要你把命令执行过去,这里我们使用下面的命令安装环境:

sudo apt-get install lzop libssl-dev 

下面我们还需要安装一个镜像转换工具,因为原本编译出来的文件是Zimage,我们的STM32MP157是没法启动的,我们需要使用这个工具在原本的Zimage的前面添加0X40个字节的头部从而得到Uimagea镜像,我们直接使用下面的命令来安装相关的工具:

sudo apt-get install u-boot-tools 

这里大家只需要确保自己将工具安装好即可,后面程序会自动调用。

安装完编译所需的环境以后,我们需要将正点原子准备好的Linux内核,被放在了资料目录下的:“01、程序源码\01、正点原子Linux出厂系统源码”目录下,是一个名为“linux-5.4.31-g886e225be-v1.9.tar.bz2”的压缩文件。下面我们准备将这个文件传输到Ubuntu中,我们首先需要在Ubuntu一侧的用户目录下的“linux”目录下新建一个LINUX的目录:

然后我们再在这个名为“LINUX”的目录下新建一个名为“atk-linux”的目录用于存放正点原子的Linux源码:

这里的命令比较简单,就不多说的,大家自行操作即可。

下面我们再将文件传输到“atk-linux”目录中,这里不管大家用什么办法只需要将文件传输过去即可,这里我使用的是powershell中的sftp,大家用自己熟悉的方式即可:

传输完成以后如图所示:

传输完成以后,我们继续回到Ubuntu的终端中,我们在终端中使用下面的命令解压正点原子的Linux内核源码:

tar -xvf linux-5.4.31-g886e225be-v1.9.tar.bz2

这里因为Linux内核,所以内容比较多,解压时间也比较长,大家耐心等待即可。 

解压完成以后,我们得到以下文件和文件夹:

和之前编译Uboot一样,我们在这个目录下新建一个shell文件来帮我们编译,这里使用下面的命令在“atk-linux”目录下新建一个shell文件:

touch build

新建完成以后我们使用下面的命令给这个文件可执行权限:

chmod +x build

这里大家将下面的命令复制到刚才创建的文件中:

#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- stm32mp1_atk_defconfig
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- uImage dtbs LOADADDR=0XC2000040 -j16

这里使用的是十六线程进行编译,大家可以根据自己的处理器进行调整,这里的LOADADDR指定了我们Linux内核在内存中运行的位置。看过从网络启动Linux教程的小伙伴应该都知道,我们将镜像烧录到的0XC2000000,大家可能会有疑问为什么这里是0XC2000040,因为我们的Uimage是Zimage添加0x40字节得到的,所以我们真正的镜像应该在0XC2000040。将命令写入文件后如图所示:

其中“make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- distclean”语句会在每次编译时清空上次编译的文件,如果需要反复编译测试的话,建议将其删掉。

修改好文件后,我们保存退出,这里我们在终端中执行我们的shell脚本就开始编译了:

./build

在编译时可能会弹出图形化配置界面,这源自我们shell脚本中的“make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig”语句:

因为我们这里使用的是正点原子处理好的源码,所以并不需要配置什么,这里我们直接双击两下“ESC”退出图形化配置界面即可,退出以后,编译就继续了:

这里的编译时间可能比较长,大家耐心等待即可。

编译完成以后如图所示,这里大家不用纠结你编译出来的大小和我一不一样,只要编译过了就没有问题:

下面带大家看看编译出来的镜像以及设备树的位置,Uimage被放在了编译目录下的“arch/arm/boot”目录下:

设备树被放在了编译目录下的“arch/arm/boot/dts”目录下,这些dtb文件就是设备树文件:

2.正点原子Linux内核烧录测试

        编译完成以后,我们来测试一下我们编译的Linux内核是否可用,这里我们先将镜像文件和设备树文件放在tftp目录准备远程拉取:

这里移动文件比较简单,大家自行移动即可。

这里我们再来到开发板一侧,ping一下我们的Ubuntu虚拟机,这里需要保证我们能ping通,这里Uboot的网络配置在之前就已经讲过了,大家自行配置即可:

这里我们直接使用下面的命令从拉取镜像并且启动:

tftp c2000000 uImage;tftp c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000 

这里我们可以看到拉取完成以后,Linux内核就启动了:

我们可以在输出的日志中看到我们编译Linux内核的时间:

这里显示的编译时间和我们实际编译的时间对应就说明我们的编译没问题。

至此,正点原子的Linux内核就编译与烧录测试就已经完成了。

3.移植Linux内核

        相信大家在编译与烧录了正点原子的Linux内核以后,我们就可以尝试自己来移植一个Linux内核了。这里Linux内核同样有三种,这里和Uboot非常类似,首先就是Linux官方发布出来的最纯正的Linux内核,然后就是ST根据自己的开发板移植的Linux内核,最后就是正点原子根据自己的开发板移植出来的Linux内核。这里我们需要在ST移植好的Linux内核的基础上进行修改,从而移植一个兼容自己开发板的Linux内核,现在让我们开始吧!

这里同样的先带大家找一下ST的Linux内核的位置,它被放在了正点原子资料目录下的“01、程序源码\05、ST官方原版Linux源码”目录下,是一个名为“en.SOURCES-stm32mp1-openstlinux-5-4-dunfell-mp1-20-06-24.tar.xz”的文件:

这里我们在原本新建的“LINUX”目录下新建一个名为“st-linux”的目录:

我们将刚才的文件传输到这个名为“st-linux”目录下,大家使用自己熟悉的传输方式即可:

传输完成以后如图所示:

这里我们直接使用下面的命令解压这个压缩包:

tar -xvf en.SOURCES-stm32mp1-openstlinux-5-4-dunfell-mp1-20-06-24.tar.xz

解压完成以后,得到以下文件夹:

我们进入“stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0”目录下,可以看到以下文件:

这里的“linux-5.4.31.tar.xz”是ST Linux的源码文件,其它的都是补丁文件,这里和Uboot是比较类似的。这里我们先使用下面的命令解压Linux源码压缩包:

tar -xvf linux-5.4.31.tar.xz

解压完成以后,我们使用下面的命令进入解压出来的目录:

我们在Linux的源码目录下使用下面的命令进行打补丁:

for p in `ls -1 ../*.patch`; do patch -p1 < $p; done

然后我们使用下面的命令生成默认的配置文件,这里和Uboot是差不多的:

make ARCH=arm multi_v7_defconfig "fragment*.config"

这里我们可以看到配置文件已经写入“.config”文件了:

我们再使用下面的两条命令将fragment config补丁打进去:

for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done 
yes '' | make ARCH=arm oldconfig

打完补丁以后,我们的ST的源码就差不多处理完成了。

这里我们在“LINUX”目录下新建一个“my-linux”目录:

这里我们将“linux-5.4.31”整个复制到“my-linux”目录下:

这里我们就可以去到“my-linux”目录下了:

这里我们再使用下面的命令将.config文件复制一份到默认配置的目录,这里默认的配置就用我们的开发板来命名:

cp .config ./arch/arm/configs/stm32mp1_atk_defconfig 

至此,我们Linux目录如图所示:

下面我们需要对相关的文件进行一些修改,这里我们使用VS Code打开目录,如图所示:

这里我们首先需要修改一下Linux项目目录下的顶层Makefile,这里我们来到大概360行处,在“ARCH        ?= $(SUBARCH)”的下方加入以下代码:

ARCH=arm
CROSS_COMPILE=arm-none-linux-gnueabihf-

这里我们再使用下面的命令新建一个用于编译的文件:

touch build

然后再使用下面的命令给予可执行权限:

chmod +x build

我们现在向“build”文件中写入下面的内容:

#!/bin/sh 
make distclean 
make stm32mp1_atk_defconfig 
make menuconfig 
make uImage dtbs LOADADDR=0XC2000040 -j16 

因为我们已经在顶层Makefile中定义编译器了,这里就不需要写编译器了。

下面我们来编译测试一下,这里我们直接使用我们编写好的“build”文件:

./build

如果你在执行命令出现了下面的错误:

可以尝试将文件第一行删掉,我们再使用下面的命令:

bash ./build

编译完成以后,如图所示:

因为正点原子参考了ST官方的STM32MP157d-ed1开发板,并且我们一开始生成.config文件时我们使用ST默认的配置文件,并且我们.config文件复制到了“stm32mp1_atk_defconfig ”文件中。我们在编译时使用到了“make stm32mp1_atk_defconfig”命令,这里的命令就会将默认的配置文件复制到.config文件中,在编译时就使用默认的配置编译。又因为正点原子的开发板参考了ST的开发板,我们所以我们使用默认的配置按理来说时可以直接启动的。这里的uimage文件被放在了老位置:

dtb文件也是一样,这里因为我们没有单独添加我们的开发板,所以这里的设备树我们就使用STM32MP157d-ed1开发板的设备树,毕竟正点原子的开发板参考了这个开发板:

这里我们同样将其复制到tftp目录下,如图:

这里我们直接在开发板一侧使用下面的命令从网络启动:

tftp c2000000 uImage;tftp c4000000 stm32mp157d-ed1.dtb;bootm c2000000 - c4000000

回车以后,我们的linux内核应该是可以正常启动的,并且同样会打印出我们编译时的时间:

这也说明了我们直接使用ST的默认配置我们的开发板是可以正常启动了,那么下面就好办了,现在我们就来把自己的开发板添加到Linux中吧!

这里我们首先需要为我们的开发板添加网卡驱动,网卡驱动被放在了正点原子资料目录下的“01、程序源码\08、模块驱动源码\01、YT8511驱动源码\linux内核下修改方法\linux”目录下:

这里的drivers和include文件夹中分别存放了网卡驱动的.c和.h文件:、

这里我们实际上也只需要使用到.c和.h文件,大家只需要将这两个文件找到即可。我们这里将.c文件拷贝到linux源码目录下的“drivers/net/phy”目录下:

然后将.h文件放在linux源码目录下的“include/linux”目录下:

修改完成以后,我们需要修改一下Linux项目目录下的“drivers/net/phy/Makefile”文件,我们在文件的最后一行加入以下语句:

obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o 

这里我们还需要修改linux项目目录下的“drivers/net/phy/Kconfig”文件,在文件中的494行左右的位置加入以下内容:

config MOTORCOMM_PHY 
   tristate "Motorcomm PHYs" 
   ---help--- 
     Supports the YT8010, YT8510, YT8511, YT8512 PHYs. 

这里需要注意的是,我们需要将内容加到“endif # PHYLIB”的上面,如图:

最后,我们在Linux的项目目录下输入下面的命令启动图形化配置:

make menuconfig

如果你这里图形化配置启动失败或者是遇到问题可以去看之前讲Uboot图形化配置的文章,里面讲解了Kconfig为什么要这样写,以及图像化加载的原理和基本操作。 

这里我们首先进入“Device Drivers”:

然后我们再进入“Network device support”:

然后我们再进入“PHY Device support and infrastructure”:

 这里我们将“Motorcomm PHYs”使能:

 如果你的配置列表中没有“Motorcomm PHYs”可以尝试去检查以下刚才修改的文件保存没有。

使能完成以后,我们保存配置,注意,这一步很重要:

配置完成以后,我们就可以多按几次“ESC”退出图形化配置。

下面我们需要来为我们的开发板添加设备树文件,因为我们并没有学习Linux驱动开发,并不知道设备树文件应该怎么写。设备树文件我们就直接复制正点原子的。这里我们需要用到两个文件,分别是“stm32mp157d-atk.dts”和“stm32mp157d-atk.dtsi”,这里我们直接使用下面的命令将“atk-linux”目录下的相关设备树文件复制到我们的linux内核目录中,使用下面的命令即可:

这里就不写命令了,大家根据自己的情况自行复制即可。

我们现在有了设备树文件,还需要让编译器编译我们新的设备树文件,我们打开linux项目目录下的“arch/arm/boot/dts/Makefile”文件:

这里我们找到“dtb-$(CONFIG_ARCH_STM32)”这一行:

我们在这个配置项的下方添加:

stm32mp157d-atk.dtb

添加好以后如图所示:

这里添加完成以后直接保存即可。

后续在LInux驱动开发时,可能会涉及内核验证,给我们的开发带来不便,我们这里直接将内核验证给关掉,这里我们打开linux项目目录下的“arch/arm/configs/”目录下的“stm32mp1_atk_defconfig”文件,如图所示:

这里我们需要将下面的四个项,使用“#”注释掉:

CONFIG_MODULE_SIG=y 
CONFIG_MODULE_SIG_ALL=y 
CONFIG_MODULE_SIG_SHA256=y 
CONFIG_MODULE_SIG_HASH="sha256" 

注释掉以后,直接保存即可。

做完以后,我们直接再次调用我们一开始编写好的“build”文件进行编译:

bash ./build

出现图形化配置我们直接关掉即可,现在我们已经不用配置任何内容了。

等待编译完成即可:


编译完成以后,我们再去找相关文件,这里的uimage文件肯定会正常生成的:

然后就是dtb文件,我们的开发板专门会生成一个dtb文件:

我们这两个文件都复制到tftp目录:

这里可以看到我们编译的内核是可以启动的(虽然本来就能启动):

至此,我们移植Linux内核就已经完成了。

五、结语

        比起前面的那些文章,这篇文章已经算比较简短了,但是引用了前面的内容,总的来说内容还是比较多的。我们前面进行了那么多准备,终于将Linux内核移植好了,当然,现在也还不能进行Linux驱动开发,我们还需要移植一个跟文件系统。但是这是后面的事了,那么最后,感谢大家的观看!