1)实验平台:正点原子STM32MP157开发板
2) 章节摘自【正点原子】《STM32MP157嵌入式Linux驱动开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=629270721801
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyzmp157.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32MP157技术交流群:691905614
第十七章 Linux内核移植
前两章我们简单了解了一下Linux内核顶层Makefile和Linux内核的启动流程,本章我们就来学习一下如何将ST官方提供的Linux内核移植到正点原子的STM32MP157开发板上。通过本章的学习,我们将掌握如何将半导体厂商提供的Linux BSP包移植到我们自己的平台上。
17.1 Linux内核初次编译
17.1.1 编译正点原子出厂Linux源码
1、第三方库安装
编译内核之前需要先在ubuntu上安装lzop库,否则内核编译会失败!命令如下:
- sudo apt-get update //先更新在安装,防止安装的时候报错
- sudo apt-get install lzop
- sudo apt-get install libssl-dev
复制代码
2、mkimage工具安装
STM32MP1编译出来的Linux内核镜像文件为uImage,这是uboot所使用的内核镜像格式,通过在zImage镜像的前面添加0X40个字节的头部来得到uImage,这个需要mkimage工具来完成此工作。所以我们需要在Ubuntu下安装mkimage工具,输入如下命令:
- sudo apt-get install u-boot-tools
复制代码
3、编译Linux系统
首先编译一下正点原子出厂的Linux源码,在Ubuntu下新建一个名为“alientek_linux”的目录存放正点原子出厂Linux源码,正点原子出厂Linux系统源码已经放到了开发板光盘中,路径为:开发板光盘1、程序源码1、正点原子Linux出厂系统源码linux-5.4.31-gb8d3ec3ac-v1.1.tar.bz2,将正点原子出厂linux系统源码拷贝到前面在Ubuntu下新建的“alientek_linux”目录下,拷贝完成以后使用如下命令解压缩:
- cd alientek_linux //进入到alientek_linux目录
- tar -vxjf linux-5.4.31-gb8d3ec3ac-v1.1.tar.bz2 //解压缩
复制代码
解压完成以后的如图17.1.1.1所示:
图17.1.1.1 正点原子提供的Linux源码根目录
在图17.1.1.1中Linux源码根目录下新建名为“stm32mp157d_atk.sh”的shell脚本,然后在这个shell脚本里面输入如下所示内容:
示例代码17.1.1.1 stm32mp157d_atk.sh文件内容
- 1 #!/bin/sh
- 2 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- distclean
- 3 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- stm32mp1_atk_defconfig
- 4 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig
- 5 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- uImage dtbs LOADADDR=0XC2000040 -j16
复制代码
第2行,执行“make distclean”,清理工程,所以stm32mp157d_atk.sh每次都会清理一下工程。如果通过图形界面配置了Linux,但是还没保存新的配置文件,那么就要慎重使用stm32mp157d_atk.sh编译脚本了,因为它会把你的配置信息都删除掉!
第3行,执行“make xxx_defconfig”,配置工程,这里使用的配置文件为stm32mp1_atk_defconfig
第4行,执行“make menuconfig”,打开图形配置界面,对Linux进行配置,如果不想每次编译都打开图形配置界面的话可以将这一行删除掉。
第5行,编译Linux内核,后面的“uImage”表示编译uImage格式的Linux内核,“dtbs”表示编译设备树,LOADADDR表示Linux内核在DDR中的加载地址为0XC2000040。
stm32mp157d_atk.sh编译脚本每次执行都是先清理整个工程,然后在重新全编译,这个过程很费时间,如果电脑配置差的话要编译很久。除了第一次编译Linux源码,我们很少清理工程全编译的,后续驱动开发很少用“make distclean”来清理工程,都是直接“make uImage LOADADDR=0XC2000040”编译内核或者“make dtbs”编译设备树。
使用chmod给予stm32mp157d_atk.sh可执行权限,然后运行此shell脚本,命令如下:
- chmod 777 stm32mp157d_atk.sh //给予可执行权限
- ./ stm32mp157d_atk.sh //执行编译脚本
复制代码
编译的时候会弹出Linux图形配置界面,如图17.1.1.2所示:
图17.1.1.2 Linux图形配置界面
Linux的图行界面配置和uboot是一样的,这里我们不需要做任何的配置,直接按两下ESC键退出,退出图形界面以后会自动开始编译Linux,等待编译完成。编译完成以后如图17.1.1.3所示:
图17.1.1.3 Linux内核编译成功
编译完成以后就会在arch/arm/boot这个目录下生成一个叫做uImage的文件,uImage就是我们要用的Linux镜像文件,如图17.1.1.4所示:
图17.1.1.4 编译得到uImage
另外也会在arch/arm/boot/dts下生成很多.dtb文件,这些.dtb就是设备树文件,我们需要的是stm32mp157d-atk.dtb这个文件,如图17.1.1.5所示:
图17.1.1.5 编译出来的dtb文件
在编译脚本stm32mp157d_atk.sh里面,每条编译命令我们都指定ARCH和CROSS_COMPILE这两个变量的值,如果自己单独运行编译命令的话输入起来会很麻烦,我们可以直接在Linux内核源码的Makefile文件里面指定ARCH和CROSS_COMPILE这两个变量的值,打开Linux内核源码目录下的主Makefile,按照如图17.1.1.6所示设置ARCH和CROSS_COMPILE:
图17.1.1.6 变量ARCH和CROSS_COMPILE
在主Makefile中添加ARCH和CROSS_COMPILE以后,stm32mp157d_atk.sh就可以简化为:
示例代码17.1.1.2 简化后的stm32mp157d_atk.sh文件内容
- 1 #!/bin/sh
- 2 make distclean
- 3 make stm32mp1_atk_defconfig
- 4 make menuconfig
- 5 make uImage dtbs LOADADDR=0XC2000040 -j16
复制代码
如果要单独编译uImage的话可以使用如下命令:
- make uImage LOADADDR=0XC2000040
复制代码
单独编译设备树的话使用如下命令:
可以看出,Linux的编译过程基本和uboot一样,都要先执行“make xxx_defconfig”来配置一下,然后在执行“make uImage”进行编译吗,如果需要使用图形界面配置的话就执行“make menuconfig”。
17.1.2 运行测试
上一小节我们已经成功编译了正点原子出厂Linux系统,得到了uImage和对应的stm32mp157d-atk.dtb设备树,本节我们就测试一下两个文件能不能正常启动,将uImage和stm32mp157d-atk.dtb这两个文件发送到Ubuntu的TFTP服务器目录下。启动开发板,uboot通过tftp命令从Ubuntu中uImage和stm32mp157d-atk.dtb并启动。为了不用每次手动输入命令,我们可以直接设置uboot中的bootcmd环境变量为:
- setenv bootcmd 'tftp c2000000 uImage;tftp c4000000 stm32mp157d-atk.dtb;bootm c2000000 - (有空格) c4000000'
- saveenv
复制代码
启动以后Linux输出如图17.1.2.1所示信息:
图17.1.2.1 Linux内核启动log信息
从图17.1.2.1可以看出,Linux内核已经运行成功,并且当前Linux内核编译时间为2020年12月11日16:52:12。
注意!如果出现如图17.1.2.2所示“end Kernel panic”错误为正常现象!
图17.1.2.2 根文件系统缺失错误
图17.1.2.2中的错误是由于根文件系统缺失导致的,也就是我们没有指定根文件系统,根文件系统的制作我们后面会详细讲解。
17.2 编译ST官方Linux系统
上一节我们编译了正点原子提供的出厂系统,主要是学习一下Linux系统的编译流程,我们最终只要将ST原厂提供的Linux系统移植到正点原子的STM32MP157开发板上,因此我们肯定要先编译一下ST原厂的系统。
17.2.1 ST官方Linux源码打补丁
1、获取到ST官方Linux源码
首先肯定要获取到ST官方的Linux源码,这个已经在6.1.1小节获取到了,进入到对应的Linux源码目录,命令如下:
- cd /home/zuozhongkai/linux/atk-mp1/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0 //进入ST官方Linux源码
复制代码
ST官方Linux源码如图17.2.1.1所示:
图17.2.1.1 ST官方Linux源码
从图17.2.1.1可以看出,ST官方的Linux源码包有各种.patch补丁文件以及Linux源码压缩包,注意细心的朋友可能观察到了,图17.1.1.1中有4个以“fragment”开头的.config文件,这些文件是默认配置文件的补丁文件,用来生成默认配置文件,稍后会讲解怎么操作。
首先解压图17.2.1.1中“linux-5.4.31.tar.xz”这个真正的源码包压缩包,解压命令如下:
- tar -vxf linux-5.4.31.tar.xz
复制代码
解压完成以后就会得到一个名为“linux-5.4.31”的Linux源码文件夹,如图17.2.1.2所示:
图17.2.1.2 解压得到Linux源码
2、内核打补丁
上面已经解压出来了Linux的源码文件,接下来就要对其打补丁,进入到上面解压出来的linux-5.4.31目录,然后执行相应的打补丁命令:
- cd linux-5.4.31/ //进入Linux源码目录
- for p in `ls -1 ../*.patch`; do patch -p1 < $p; done //打补丁
复制代码
打补丁结果如图17.2.1.3所示:
图17.2.1.3 打补丁结果
3、生成默认配置文件
编译linux内核的时候也需要使用“make xxx_defconfig”来对其进行默认配置,比如编译正点原子出厂Linux系统的时候我们使用了“make stm32mp1_atk_defconfig”,其中stm32mp1_atk_defconfig就是正点原子开发板使用的默认配置文件。但是ST官原厂Linux内核需要先生成默认配置文件,并且对其进行打补丁,进入Linux内核源码根目录下,然后执行如下命令:
- cd linux-5.4.31/ //进入到linux内核
- make ARCH=arm multi_v7_defconfig "fragment*.config" //生成默认配置文件
复制代码
完成以后结果如图17.2.1.4所示:
图17.2.1.4 生成.config文件
上面我们只是在Linux源码根目录下生成了.config配置文件,如图17.2.1.5所示:
图17.2.1.5 .config文件
.config文件非常重要,Linux内核的所有配置项最终都保存在.config文件里面,最终编译Linux内核的时候需要读取.config里面的配置项!此时我们只是生成了.config,还并没有将图17.1.1.1中的这些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
复制代码
第1条命令是将图17.1.2.1中的fragment config补丁打到.config文件里面。
第2条命令是对oldconfig的所有选项都选择yes,这些选项最终都会写入到.config里面。注意,这条命令yes后面是两个‘'’,而不是一个“''”,不要输入错了!!!
完成以后如图17.2.1.6所示:
图17.2.1.6 配置完成的.config
至此,Linux源码根目录下的.config文件就已经保存了所有的配置项,所以只需要复制一份.config作为我们的默认配置文件即可,复制命令如下:
- cp .config ./arch/arm/configs/stm32mp1_atk_defconfig
复制代码
此时在Linux内核的/arch/arm/configs目录下存在一个名为stm32mp1_atk_defconfig的默认配置文件,如图17.2.1.7所示:
图17.2.1.7默认配置文件
至此,Linux内核全部打完补丁,linux-5.4.31目录就是我们要移植的Linux源码,但是图17.2.1.3中的linux-5.4.31目录路径有点长,不适合阅读和编译。所以我们新建一个名为“my_linux”的目录来保存我们要移植的linux源码,然后将打完补丁的linux源码linux-5.4.31拷贝到“my_linux”目录下,命令如下:
- cd /home/zuozhongkai/linux/atk-mp1/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0
- cp linux-5.4.31 /home/zuozhongkai/linux/atk-mp1/linux/my_linux/ -rf //拷贝
复制代码
拷贝完成以后的my_linux目录如图17.2.1.8所示:
图17.2.1.8 拷贝过来的linux源码
最后就是创建vscode工程,方便我们移植和阅读。
17.2.2 编译ST官方Linux源码
上一小节已经准备好了ST官方Linux源码,存放到了my_ulinux目录下,本小节我们来编译一下这个Linux源码。
1、修改Makefile
首先修改一下Linux源码的Makefile文件,也可以不修改,但是在编译的时候需要多输入一些参数,为了偷懒,还是修改一下。修改方法已经在17.1.1小节讲解过了,就是在Makefile文件里面添加ARCH和CROSS_COMPILE这两个变量的值,如图17.2.2.1所示:
图17.2.2.1 设置ARCH和CROSS_COMPILE变量值
ST官方Linux肯定适配了官方的STM32MP1 EVK开发板,我们就编译EVK开发板对应的Linux内核和设备树,编译完成以后在正点原子的STM32MP1开发板启动,看看能不能运行,不能的话就要修改相应的文件,这就是Linux的移植。创建一个名为“stm32mp157d_atk.sh”的编译脚本,脚本内容如下:
示例代码17.1.2.1 stm32mp157d_atk.sh文件内容
- 1 #!/bin/sh
- 2 make distclean
- 3 make stm32mp1_atk_defconfig
- 4 make menuconfig
- 5 make uImage dtbs LOADADDR=0XC2000040 -j16
复制代码
给予stm32mp157d_atk.sh可执行文件,然后运行此编译脚本,命令如下:
- chmod 777 stm32mp157d_atk.sh //给予可执行权限,仅第一次
- ./stm32mp157d_atk.sh //运行编译脚本
复制代码
等待编译完成,编译完成以后的到uImage镜像文件和设备树,其中STM32MP157系列的设备树有很多,如图17.2.2.3所示:
图17.2.2.3 编译得到的.dtb文件
从图17.2.2.3可以看出,此时有很多.dtb文件,我们这里使用stm32mp157d-ed1.dtb这个文件。
17.2.3 启动测试
上一小节我们已经编译出来了ST官方开发板对应的uImage和stm32mp157d-ed1.dtb设备树,本章我们直接在正点原子的STM32MP157开发板上运行,看看ST官方系统能不能运行下去。讲uImage和stm32mp157d-ed1.dtb发送到Ubuntu下的tftp服务器目录下,然后通过uboot的tftp命令下载并启动,Linux系统运行log信息如图17.2.3.1所示:
图17.2.3.1 ST官方Linux系统运行结果
从图17.2.3.1可以看出,ST官方开发板的Linux系统在正点原子STM32MP157开发板上启动成功,所以后续的移植就要简单的多(实际不需要移植,直接在ST官方开发板相应文件上修改即可,但是为了学习,我们还是学习一下如何在Linux源码里面添加我们自己的开发板),只需要参考ST官方开发板创建一个设备树即可。
17.3 在Linux中添加自己的开发板
17.3.1 添加开发板对应的默认配置文件
首先就是添加开发板对应的默认配置文件,这里直接使用17.2.1小节制作出来的stm32mp1_atk_defconfig文件即可。
17.3.2 添加开发板对应的设备树
1、新建相应的设备树文件
上面我们使用ST官方开发板的设备树已经成功启动了Linux系统,但是我们在实际开发中肯定要添加一份自己开发板所对应的设备树。正点原子STM32MP157开发板是以ST官方的开发板为蓝本制作的,所以设备树也是参考ST官方开发板的。在arch/arm/boot/dts/目录下新建名为“stm32mp157d-atk.dtsi”的设备树头文件,然后将stm32mp15xx-edx.dtsi文件里面的内容全部复制到stm32mp157d-atk.dtsi里面。
再新建一个名为“stm32mp157d-atk.dts”的文件,将stm32mp157c-ed1.dts文件里面的内容都拷贝到stm32mp157d-atk.dts里面,如图17.3.2.1所示:
图17.3.2.1 stm32mp157d-atk.dts文件内容
记得一定要将图17.3.2.1中14行原来的“stm32mp15xx-edx.dtsi”头文件改为“stm32mp15d-atk.dtsi”!
2、修改stm32mp157d-atk.dtsi文件
stm32mp157d-atk.dtsi里面的内容是直接复制的ST官方开发板的stm32mp15xx-edx.dtsi,所以里面的很多配置是针对ST官方开发板的,比如PMIC芯片,这些配置是不需要的,要删除掉。正点原子开发板没有用到集成PMIC芯片,因此还需要在设备树里面添加一些电源节点信息,修改完成以后的stm32mp157d-atk.dtsi文件内容如下所示:
示例代码17.3.2.1 stm32mp157d_atk.dtsi文件内容
- /*
- 1 * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
- 2 * Author: Ludovic Barre <<a href="mailto:ludovic.barre@st.com">ludovic.barre@st.com</a>> for ST.
- 3 */
- 4 #include "stm32mp157-m4-srm.dtsi"
- 5 #include "stm32mp157-m4-srm-pinctrl.dtsi"
- 6 #include <dt-bindings/gpio/gpio.h>
- 7 #include <dt-bindings/mfd/st,stpmic1.h>
- 8
- 9 / {
- 10 memory@c0000000 {
- 11 device_type = "memory";
- 12 reg = <0xC0000000 0x40000000>;
- 13 };
- 14
- 15 reserved-memory {
- 16 #address-cells = <1>;
- 17 #size-cells = <1>;
- 18 ranges;
- 19
- 20 mcuram2: mcuram2@10000000 {
- 21 compatible = "shared-dma-pool";
- 22 reg = <0x10000000 0x40000>;
- 23 no-map;
- 24 };
- 25
- 26 vdev0vring0: vdev0vring0@10040000 {
- 27 compatible = "shared-dma-pool";
- 28 reg = <0x10040000 0x1000>;
- 29 no-map;
- 30 };
- 31
- 32 vdev0vring1: vdev0vring1@10041000 {
- 33 compatible = "shared-dma-pool";
- 34 reg = <0x10041000 0x1000>;
- 35 no-map;
- 36 };
- 37
- 38 vdev0buffer: vdev0buffer@10042000 {
- 39 compatible = "shared-dma-pool";
- 40 reg = <0x10042000 0x4000>;
- 41 no-map;
- 42 };
- 43
- 44 mcuram: mcuram@30000000 {
- 45 compatible = "shared-dma-pool";
- 46 reg = <0x30000000 0x40000>;
- 47 no-map;
- 48 };
- 49
- 50 retram: retram@38000000 {
- 51 compatible = "shared-dma-pool";
- 52 reg = <0x38000000 0x10000>;
- 53 no-map;
- 54 };
- 55 };
- 56
- 57 vddcore: buck1 {
- 58 compatible = "regulator-fixed";
- 59 regulator-name = "vddcore";
- 60 regulator-min-microvolt = <1200000>;
- 61 regulator-max-microvolt = <1350000>;
- 62 regulator-always-on;
- 63 regulator-boot-on;
- 64 };
- 65
- 66 v3v3: regulator-3p3v {
- 67 compatible = "regulator-fixed";
- 68 regulator-name = "v3v3";
- 69 regulator-min-microvolt = <3300000>;
- 70 regulator-max-microvolt = <3300000>;
- 71 regulator-always-on;
- 72 regulator-boot-on;
- 73 };
- 74 };
- 75
- 76 &cpu0{
- 77 cpu-supply = <&vddcore>;
- 78 };
- 79
- 80 &crc1 {
- 81 status = "okay";
- 82 };
- 83
- 84 &dma1 {
- 85 sram = <&dma_pool>;
- 86 };
- 87
- 88 &dma2 {
- 89 sram = <&dma_pool>;
- 90 };
- 91
- 92 &dts {
- 93 status = "okay";
- 94 };
- 95
- 96 eernet0 {
- 97 status = "okay";
- 98 pinctrl-0 = <eernet0_rgmii_pins_a>;
- 99 pinctrl-1 = <eernet0_rgmii_pins_sleep_a>;
- 100 pinctrl-names = "default", "sleep";
- 101 phy-mode = "rgmii-id";
- 102 max-speed = <1000>;
- 103 phy-handle = <&phy0>;
- 104
- 105 mdio0 {
- 106 #address-cells = <1>;
- 107 #size-cells = <0>;
- 108 compatible = "snps,dwmac-mdio";
- 109 phy0: ethernet-phy@0 {
- 110 reg = <0>;
- 111 };
- 112 };
- 113 };
- 114
- 115 &hash1 {
- 116 status = "okay";
- 117 };
- 118
- 119 &ipcc {
- 120 status = "okay";
- 121 };
- 122
- 123 &iwdg2 {
- 124 timeout-sec = <32>;
- 125 status = "okay";
- 126 };
- 127
- 128 &rng1 {
- 129 status = "okay";
- 130 };
- 131
- 132 &rtc {
- 133 status = "okay";
- 134 };
- 135
- 136 &sdmmc1 {
- 137 pinctrl-names = "default", "opendrain", "sleep";
- 138 pinctrl-0 = <&sdmmc1_b4_pins_a>;
- 139 pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
- 140 pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;
- 141 broken-cd;
- 142 st,neg-edge;
- 143 bus-width = <4>;
- 144 vmmc-supply = <&v3v3>;
- 145 status = "okay";
- 146 };
- 147
- 148 &sdmmc2 {
- 149 pinctrl-names = "default", "opendrain", "sleep";
- 150 pinctrl-0 = <&sdmmc2_b4_pins_a>;
- 151 pinctrl-1 = <&sdmmc2_b4_od_pins_a>;
- 152 pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>;
- 153 non-removable;
- 154 st,neg-edge;
- 155 bus-width = <8>;
- 156 vmmc-supply = <&v3v3>;
- 157 keep-power-in-suspend;
- 158 status = "okay";
- 159 };
- 160
- 161 &sram {
- 162 dma_pool: dma_pool@0 {
- 163 reg = <0x50000 0x10000>;
- 164 pool;
- 165 };
- 166 };
- 167
- 168 &uart4 {
- 169 pinctrl-names = "default", "sleep", "idle";
- 170 pinctrl-0 = <&uart4_pins_a>;
- 171 pinctrl-1 = <&uart4_sleep_pins_a>;
- 172 pinctrl-2 = <&uart4_idle_pins_a>;
- 173 pinctrl-3 = <&uart4_pins_a>;
- 174 /delete-property/dmas;
- 175 /delete-property/dma-names;
- 176 status = "okay";
- 177 };
复制代码
3、编译stm32mp157d-atk.dts设备树
最后我们肯定要编译stm32mp157d-atk.dts,得到对应的.dtb文件,打开arch/arm/boot/dts/Makefile,到“dtb-$(CONFIG_ARCH_STM32)”配置项,在此配置项中加入“stm32mp157d-atk.dtb”,完成以后如图17.3.2.2所示:
图17.3.2.2 Makefile文件
图17.3.2.2中第1011行添加了“stm32mp157d-atk.dtb”,表示编译的时候也将stm32mp157d-atk.dts编译为stm32mp157d-atk.dtb。
17.3.3 关闭内核模块验证
后续做Linux驱动实验的时候我们都是编译驱动模块,然后在系统里面加载,加载的时候系统会验证模块,有时候会验证出错。比如板子运行的系统和编译驱动模块所用的系统不一致的时候,这样为我们的学习带来了很大的不便,为了方便开发,我们可以关闭内核模块验证。打开默认配置文件stm32mp1_atk_defconfig,里面有如下所示配置项目:
- CONFIG_MODULE_SIG=y
- CONFIG_MODULE_SIG_ALL=y
- CONFIG_MODULE_SIG_SHA256=y
- CONFIG_MODULE_SIG_HASH="sha256"
复制代码
将上面4个配置项屏蔽掉,如图17.3.3.1所示:
图17.3.3.1 关闭内核模块验证功能
另外,我们也可以直接在Linux的图形化配置界面上关闭掉内核模块验证,输入如下命令打开Linux内核图形化配置界面:
配置路径如下:
- -> Enable loadable module support (MODULES [=y])
- ->Module signature verification
复制代码
取消对“Module signature verification”选项的选中,如图17.3.3.2所示:
图17.3.3.2 取消内核模块验证
配置完成以后退出,打开.config文件,查看还有没有CONFIG_MODULE_SIG配置项,如图17.3.3.3所示:
图17.3.3.3 .config文件
从图17.3.3.3可以看出,COFIG_MODULE_SIG选项没有设置,同样的CONFIG_MODULE_SIG_ALL、CONFIG_MODULE_SIG_SHA256和CONFIG_MODULE_SIG_HASH也一起消失了。
最后我们需要将配置项保存到stm32mp1_atk_defconfig里面,打开图形化配置界面,选择<Save>项目,将修改后的所有配置项保存到stm32mp1_atk_defconfig文件里面,如图17.3.3.4所示:
图17.3.3.4 保存默认配置
后续试验中,只要通过图形化界面修改了Linux内核配置,最好及时将其保存到stm32mp1_atk_defconfig文件。因为图形化界面修改的配置只是暂时保存到.config文件里面,一旦使用“make clean”清理工程,那么.config文件就会被删除掉,所有的配置也就丢失了!
17.3.4 关闭内核log信息时间戳
大家可以仔细观察会发现Linux系统启动的时候每行信息前面都会打印出相应的时间,如图17.3.4.1所示:
图17.3.4.1 log时间信息输出
从图17.3.4.1可以看出,每行log信息前面都有时间信息,在调试的时候这个时间信息看起来比较“烦人”我们可以通过配置内核将其关闭,配置路径如下:
- -> Kernel hacking
- -> printk and dmesg options
- ->Show timing information on printks //取消选中
复制代码
如图17.3.4.1所示:
图17.3.4.1 取消printk时间戳
17.3.5 编译测试
设备树修改好以后就可以编译了,因为我们只是修改了设备树,所以编译脚本直接用17.1.2小节创建的stm32mp157d_atk.sh编译脚本即可!
编译完成以后在arch/arm/boot目录下生成uImage镜像,在arch/arm/boot/dts目录下生成stm32mp157d-atk.dtb文件,将这两个文件拷贝到tftp服务器目录下,然后在uboot中使用tftp命令下载并运行,命令如下:
- tftp c2000000 uImage
- tftp c4000000 stm32mp157d-atk.dtb
- bootm c2000000 - c4000000
复制代码
当出现如图17.3.5.1所示的log信息以后就说明Linux启动运行成功:
图17.3.5.1 Linux运行log信息
17.4 烧写系统镜像到EMMC里面
上面已经测试过了Linux系统镜像和设备树,但是我们是通过tftp命令从网络上下载测试的,实际产品开发中最终是要将系统烧写到外部Flash中的,比如EMMC。本节我们来学习一下如何将uIamge和stm32mp157d-atk.dtb打包成ext4格式,然后通过STM32CubeProgrammer烧写到EMMC里面,最终启动EMMC里面的Linux系统。
17.4.1 系统镜像打包
首先就是将uImage和stm32mp157d-atk.dtb打包在一起,格式为ext4格式。当然了,你也可以向这个ext4格式打包文件里面添加其他的内容,比如图片等,本节我们只用到uImage和stm32mp157d-atk.dtb。
打包过程是在Ubuntu下完成的,首先新建一个名为“bootfs”的文件夹,然后将uImage和stm32mp157d-atk.dtb这两个文件放到bootfs文件夹下,如图17.4.1.1所示:
图17.4.1.1 bootfs文件夹
1、新建ext4格式磁盘
首先新建一个ext4格式的磁盘,然后挂载这个ext4格式的磁盘,将stm32mp157d-atk.dtb和uImage拷贝到这个ext4磁盘即可。
进入到bootfs文件夹,然后输入如下命令创建ext4磁盘:
示例代码17.4.1.1 ext4磁盘创建命令
- 1 cd bootfs
- 2 dd if=/dev/zero of=bootfs.ext4 bs=1M count=10
- 3 mkfs.ext4 -L bootfs bootfs.ext4
复制代码
第1行,进入到图17.4.1.1中的bootfs目录。
第2行,使用dd命令创建一个名为bootfs.ext4的磁盘,of指定磁盘名字为“bootfs.ext4”;bs指定磁盘输入/输出块大小为1MB;count指定磁盘的块数量为10个。因此bootfs.ext4只能存放不超过10MB的文件,如果要存放的文件总大小超过10MB,那么就要适当调整count参数的大小。
第3行,使用mkfs.ext4将bootfs.ext4磁盘格式化为ext4格式。
完成以后就会生成名为“bootfs.ext4”的磁盘,如图17.4.1.2所示:
图17.4.1.2 bootfs.ext4磁盘
2、将系统镜像拷贝到ext4磁盘中
首先创建一个目录用来挂载前面制作制作出来的bootfs.ext4,比如我这里创建目录/mnt/bootfs,命令如下:
sudo mkdir /mnt/bootfs
接下来使用mount命令将bootfs.ext4挂载到/mnt/bootfs目录下,命令如下:
- cd /home/zuozhongkai/linux/atk-mp1/linux/bootfs
- sudo mount bootfs.ext4 /mnt/bootfs/
复制代码
挂载成功以后就将图17.4.1.1中的uImage和stm32mp157d-atk.dtb拷贝到/mnt/bootfs目录下,命令如下:
- cd /home/zuozhongkai/linux/atk-mp1/linux/bootfs
- sudo cp uImage stm32mp157d-atk.dtb /mnt/bootfs/
复制代码
拷贝完成以后使用umount卸载/mnt/bootfs即可,命令如下:
至此,uImage和stm32mp157d-atk.dtb就已经打包到了图17.4.1.2中的bootfs.ext4中,稍后使用STM32CubeProgrammer软件将其烧写到EMMC里面。烧写之前最好在Windows下打开bootfs.ext4看一下,看看是否已经将uImage和stm32mp157d-atk.dtb打包进去,如图17.4.1.3所示:
图17.4.1.3 bootfs.ext4
17.4.2 烧写到EMMC
接下来就是将上一小节打包好的ext4格式的bootfs.ext4烧写到开发板的EMMC里面,使用STM32CubeProgrammer软件完成此操作。将bootfs.ext4拷贝到以前创建的images目录下,如图17.4.2.1所示:
图17.4.2.1 images目录
修改flashlayout文件tf-a.tsv,在后面加入bootfs.ext4的烧写脚本,如图17.4.2.2所示:
图17.4.2.2 修改后的flashlayout
图17.4.2.2中第7行就是bootfs.ext4的烧写脚本,设置好以后就可以使用STM32CubeProgrammer烧写系统了,烧写完成以后设置拨码开关从EMMC启动,启动以后进入uboot的命令行,输入如下命令查看EMMC分区2里面是否正确烧写了uImage和stm32mp157d-atk.dtb:
结果如图17.4.2.3所示:
图17.4.2.3 EMMC分区2
从图17.4.2.3可以看出,此时EMMC的分区2存在uImage和stm32mp157d-atk.dtb这两个文件,设置bootcmd环境变量,从EMMC里面读取系统镜像和设备树并启动,命令如下:
- setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
- saveenv
- boot
复制代码
如果烧写正常的话就会从EMMC的分区2中加载系统并运行,如图17.4.2.4所示:
图17.4.2.4 EMMC启动系统
可以看出,将系统镜像烧写到EMMC里面还是很繁琐的,因此一般都是在产品最终开发完成,出厂的时候才会烧写到EMMC里面。在调试阶段都不会烧写到EMMC里面,而是在uboot里面使用tftp或nfs命令通过网络下载系统镜像并运行,这样当我们修改了系统以后只需要将系统镜像复制到tftp或nfs服务器目录即可,极大的简化开发方式。
17.5 根文件系统缺失错误
Linux内核启动以后是需要根文件系统的,根文件系统存在哪里是由uboot的bootargs环境变量指定的,它会传递给Linux内核作为命令行(command line)参数。比如13.4.2小节设置root=/dev/mmcblk2p3,也就是说根文件系统存储在/dev/mmcblk2p3中,也就是EMMC的分区3中。如果我们不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?这个问题是很常见的,我们在实际的工作中开发一个产品,这个产品的第一版硬件出来以后我们是没有对应的根文件系统可用的,必须要自己做根文件系统。在没有根文件系统的情况下,Linux内核启动的时候就会有如图17.5.1所示的错误:
图17.5.1 根文件系统缺失错误
在图17.5.1中最后一行为:
- end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
复制代码
也就是提示内核崩溃,VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。解决方法就是制作根文件系统,并且设置uboot的bootargs环境变量,指定根文件系统所在的目录。
|