正点原子嵌入式linux驱动开发——Linux内核移植

news2024/11/15 14:07:30

之前的两篇笔记,简单了解了一下Linux内核顶层 Makefile和Linux内核的启动流程,本篇内容来学习一下如何将ST官方提供的Linux内核移植到正点原子的STM32MP157开发板上。通过本章的学习,将掌握如何将半导体厂商提供的Linux BSP包移植到自己的平台上

Linux内核初次编译

编译正点原子出厂Linux源码

之前的Linux内核第一篇笔记,就是编译正点原子STM32MP157开发板出厂的Linux内核,可以去回顾一下。

运行测试

成功编译正点原子出厂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输出如下图所示信息:
Linux内核启动log信息
从上图中可以看出,Linux内核已经运行成功,且当前Linux内核编译时间为2022年12月11日16:52:12。

注意!如果出现下图中所示“end Kernel panic”错误为正常现象
根文件系统缺失错误
上图中错误是由于根文件系统缺失导致的,也就是没有指定根文件系统,根文件系统的制作之后会有详细的笔记。

编译ST官方Linux系统

上一节编译了正点原子提供的出厂系统,主要是学习一下Linux系统的编译流程,最终是要将ST原厂提供的Linux系统移植到正点原子的STM32MP157开发板上,因此肯定要先编译一下ST原厂的系统。

ST官方Linux源码打补丁

获取ST官方Linux源码

首先肯定要获取到ST官方的Linux源码,这个已经在之前的学习中获取到了,进入到对应的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源码如下图所示:
ST官方Linux源码
从上图可以看出,ST官方的Linux源码包有各种.patch补丁文件以及Linux源码压缩包 ,注意上图中有 4个以“fragment”开头的.config文件,这些文件是默认配置文件的补丁文件,用来生成默认配置文件,稍后会讲解怎么操作。

首先解压上图中“linux-5.4.31.tar.xz”这个真正的源码包压缩包,解压命令如下:

tar -vxf linux-5.4.31.tar.xz

解压完成后会得到名为“linux-5.4.31”的Linux源码文件夹,如下图所示:
解压得到Linux源码

内核打补丁

上面已经解压出来了Linux的源码文件,接下来就要对其打补丁,进入到上面解压出来的linux-5.4.31目录,然后执行相应的打补丁命令:

cd linux-5.4.31/ //进入 Linux源码目录
for p in `ls -1 ../*.patch`; do patch -p1 < $p; done //打补丁

打补丁结果如下图所示:
打补丁结果

生成默认配置文件

编译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" //生成默认配置文件

完成以后结果如下图所示:
生成.config文件
上面只是在Linux源码根目录下生成了.config配置文件,如下图所示:
.config文件
.config文件非常重要,Linux内核的所有配置项最终都保存在.config文件里面,最终编译Linux内核的时候需要读取.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

第1条命令是将源码目录下的fragment config补丁打到.config文件里面。

第2条命令是对oldconfig的所有选项都选择yes,这些选项最终都会写入到.config里面。
注意, 这条命令yes后面是两个“ ’ ”,而不是一个“ " ”,不要输入错了!!

完成后如下图所示:
配置完成的.config
至此,Linux源码根目录下的.config文件就已经保存了所有的配置项,所以只需要复制一份 .config作为默认配置文件即可,复制命令如下:

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

此时在Linux内核的/arch/arm/configs目录下存在一个名为stm32mp1_atk_defconfig的默认配置文件,如下图所示:
默认配置文件
至此,Linux内核全部打完补丁,linux-5.4.31目录就是要移植的Linux源码,但是默认的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 //拷贝

拷贝完成以后,可以创建VSCode工程,方便移植和阅读。

编译ST官方Linux源码

上一小节已经准备好了ST官方Linux源码,存放到了my_ulinux目录下,本小节来编译一下这个Linux源码。

修改Makefile

首先修改一下Linux源码的Makefile文件,也可以不修改,但是在编译的时候需要多输入一些参数,为了偷懒,还是修改一下。修改方法已经在跟之前TF-A,uboot以及Linux内核使用的时候讲解过了,就是在Makefile文件里面添加ARCH和CROSS_COMPILE这两个变量的值,如下图所示:
设置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系列的设备树有很多,如下图所示:
编译得到的.dtb文件
从上图可以看出,此时有很多.dtb文件,这里使用stm32mp157d-ed1.dtb这个文件。

网络驱动修改

V1.2版本核心板网络PHY芯片采用RTL8211,ST官方源码默认已经支持了RTL8211,所以不需要进行任何修改。但是V1.3以后的核心板网络PHY芯片改为了裕太电子的YT8511,ST官方源码默认没有支持YT8511,因此需要自行移植相关的网络驱动。相关驱动以及修改方法已经放到了开发板光盘中。

将motorcomm.c和motorcomm_phy.h分别拷贝到Linux源码下的drivers/net/phy和include/linux目录下。拷贝完成以后修改drivers/net/phy/Makefile文件,加上下面这句:

obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o

修改以后的Makefile如下图所示:
修改后的Makefile
另外还需要修改drivers/net/phy/Kconfig文件,加入如下内容:

示例代码17.2.3.1 Kconfig添加的内容 
1 config MOTORCOMM_PHY 
2 tristate "Motorcomm PHYs" 
3 ---help--- 
4 Supports the YT8010, YT8510, YT8511, YT8512 PHYs.

添加完成后的Kconfig文件如下图所示:
修改后的Kconfig
最后输入“make menuconfig”,打开linux内核配置界面,使能YT8511驱动,配置路径如下:

-> Device Drivers
-> Network device support (NETDEVICES [=y])
-> PHY Device support and infrastructure (PHYLIB [=y])
-> <*> Motorcomm PHYs //将 YT8511驱动编译进内核

如下图所示:
使能YT8511驱动
网络驱动修改完成后,按照上一小节的方法重新编译linux内核即可。

启动测试

上一小节已经编译出来了ST官方开发板对应的uImage和 stm32mp157d-ed1.dtb设备树,本章直接在正点原子的STM32MP157开发板上运行,看看ST官方系统能不能运行下去。将uImage和stm32mp157d-ed1.dtb发送到Ubuntu下的tftp服务器目录下,然后通过uboot的tftp命令下载并启动,Linux系统运行log信息如下图所示:
ST官方Linux系统运行结果
从上图可以看出,ST官方开发板的Linux系统在正点原子STM32MP157开发板上启动成功,所以后续的移植就要简单的多(实际不需要移植,直接在ST官方开发板相应文件上修改即可,但是为了学习,还是学习一下如何在Linux源码里面添加自己的开发板),只需要参考ST官方开发板创建一个设备树即可

在Linux中添加自己的开发板

添加开发板对应的默认配置文件

首先就是添加开发板对应的默认配置文件,这里直接使用上文中制作出来的stm32mp1_atk_defconfig文件即可。

添加开发板对应的设备树

新建相应的设备树文件

上面使用ST官方开发板的设备树已经成功启动了Linux系统,但是在实际开发中肯定要添加一份自己开发板所对应的设备树。正点原子STM32MP157开发板是以ST官方的开发板为蓝本制作的,所以设备树也是参考ST官方开发板的。在arch/arm/boot/dts/目录下新建名为“stm32mp157d-atk.dtsi”的设备树头文件,然后将stm32mp15xx-edx.dtsi文件里面的内容全部复制到 stm32mp157d-atk.dtsi里面。

再新建一个名为“stm32mp157d-atk.dts”的文件,将stm32mp157d-ed1.dts文件里面的内容都拷贝到stm32mp157d-atk.dts里面,如下图所示 :
stm32mp157d-atk.dts文件内容
记得一定要将上图中14行原来的“stm32mp15xx-edx.dtsi”头文件改为“stm32mp15d-atk.dtsi”。

修改stm32mp157d-atk.dtsi文件

tm32mp157d-atk.dtsi里面的内容是直接复制的ST官方开发板的stm32mp15xx-edx.dtsi,所以里面的很多配置是针对ST官方开发板的,比如PMIC芯片,这些配置是不需要的,要删除掉。正点原子开发板没有用到集成PMIC芯片,因此还需要在设备树里面添加一些电源节点信息,修改完成以后的stm32mp157d-atk.dtsi文件内容如下所示:

// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
 * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
 * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
 */

#include "stm32mp157-m4-srm.dtsi" 
#include "stm32mp157-m4-srm-pinctrl.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/mfd/st,stpmic1.h>

/ {
	memory@c0000000 {
		device_type = "memory";
		reg = <0xC0000000 0x40000000>;
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		mcuram2: mcuram2@10000000 {
			compatible = "shared-dma-pool";
			reg = <0x10000000 0x40000>;
			no-map;
		};

		vdev0vring0: vdev0vring0@10040000 {
			compatible = "shared-dma-pool";
			reg = <0x10040000 0x1000>;
			no-map;
		};

		vdev0vring1: vdev0vring1@10041000 {
			compatible = "shared-dma-pool";
			reg = <0x10041000 0x1000>;
			no-map;
		};

		vdev0buffer: vdev0buffer@10042000 {
			compatible = "shared-dma-pool";
			reg = <0x10042000 0x4000>;
			no-map;
		};

		mcuram: mcuram@30000000 {
			compatible = "shared-dma-pool";
			reg = <0x30000000 0x40000>;
			no-map;
		};

		retram: retram@38000000 {
			compatible = "shared-dma-pool";
			reg = <0x38000000 0x10000>;
			no-map;
		};
	};

	vddcore: buck1 {
		compatible = "regulator-fixed";
		regulator-name = "vddcore";
		regulator-min-microvolt = <1200000>;
		regulator-max-microvolt = <1350000>;
		regulator-always-on;
		regulator-boot-on;
	};

	v3v3: regulator-3p3v {
		compatible = "regulator-fixed";
		regulator-name = "v3v3";
		regulator-min-microvolt = <3300000>;
		regulator-max-microvolt = <3300000>;
		regulator-always-on;
		regulator-boot-on;
	};
};

&cpu0{
	cpu-supply = <&vddcore>;
};

&crc1 {
	status = "okay";
};

&dma1 {
	sram = <&dma_pool>;
};

&dma2 {
	sram = <&dma_pool>;
};

&dts {
	status = "okay";
};

&ethernet0 {
	status = "okay";
	pinctrl-0 = <&ethernet0_rgmii_pins_a>;
	pinctrl-1 = <&ethernet0_rgmii_pins_sleep_a>;
	pinctrl-names = "default", "sleep";
	phy-mode = "rgmii-id";
	max-speed = <1000>;
	phy-handle = <&phy0>;

	mdio0 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "snps,dwmac-mdio";
		phy0: ethernet-phy@0 {
			reg = <0>;
		};
	};
};

&hash1 {
	status = "okay";
};

&ipcc {
	status = "okay";
};

&iwdg2 {
	timeout-sec = <32>;
	status = "okay";
};

&rng1 {
	status = "okay";
};

&rtc {
	status = "okay";
};

&sdmmc1 {
	pinctrl-names = "default", "opendrain", "sleep";
	pinctrl-0 = <&sdmmc1_b4_pins_a>;
	pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
	pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;
	broken-cd;
	st,neg-edge;
	bus-width = <4>;
	vmmc-supply = <&v3v3>;
	status = "okay";
};

&sdmmc2 {
	pinctrl-names = "default", "opendrain", "sleep";
	pinctrl-0 = <&sdmmc2_b4_pins_a>;
	pinctrl-1 = <&sdmmc2_b4_od_pins_a>;
	pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>;
	non-removable;
	st,neg-edge;
	bus-width = <8>;
	vmmc-supply = <&v3v3>;
	keep-power-in-suspend;
	status = "okay";
};

&sram {
	dma_pool: dma_pool@0 {
		reg = <0x50000 0x10000>;
		pool;
	};
};

&uart4 {
	pinctrl-names = "default", "sleep", "idle";
	pinctrl-0 = <&uart4_pins_a>;
	pinctrl-1 = <&uart4_sleep_pins_a>;
	pinctrl-2 = <&uart4_idle_pins_a>;
	pinctrl-3 = <&uart4_pins_a>;
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
};

编译stm32mp157d-atk.dts设备树

最后肯定要编译stm32mp157d-atk.dts,得到对应的.dtb文件,打开arch/arm/boot/dts/Makefile,到“dtb-$(CONFIG_ARCH_STM32)”配置项,在此配置项中加入“stm32mp157d-atk.dtb”,完成以后如下图所示:
Makefile文件
上图中第1011行添加了“stm32mp157d-atk.dtb”,表示编译的时候也将stm32mp157d-atk.dts编译为stm32mp157d-atk.dtb。

关闭内核模块验证

后续做Linux驱动实验的时候都是编译驱动模块,然后在系统里面加载,加载的时候系统会验证模块,有时候会验证出错。比如板子运行的系统和编译驱动模块所用的系统不一致的时候,这样为学习带来了很大的不便,为了方便开发,可以关闭内核模块验证。打开默认配置文件stm32mp1_atk_defconfig,里面有如下所示配置项目:

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

将上面4个配置项屏蔽,如下图所示:
关闭内核模块验证功能
另外,也可以直接在Linux图形化配置界面上关掉内核验证模块,输入如下命令打开Linux图形化配置界面:

make menuconfig

配置路径如下:

-> Enable loadable module support (MODULES [=y])
->Module signature verification

取消对“Module signature verification”选项的选中,如下图所示:
取消内核模块验证
配置完成后退出,打开.config文件,查看还有没有CONFIG_MODULE_SIG配置项,如下图所示:
.config文件
从上图可以看出,COFIG_MODULE_SIG选项没有设置,同样的CONFIG_MODULE_SIG_ALL、CONFIG_MODULE_SIG_SHA256和CONFIG_MODULE_SIG_HASH也一起消失
了。

最后需要将配置项保存到stm32mp1_atk_defconfig里面,打开图形化配置界面,选择<Save>项目,将修改后的所有配置项保存到stm32mp1_atk_defconfig文件里面,如下图所示:
保存默认配置
后续试验中,只要通过图形化界面修改了Linux内核配置,最好及时将其保存到stm32mp1_atk_defconfig文件。因为图形化界面修改的配置只是暂时保存到.config文件里面,一旦使用“make clean”清理工程,那么.config文件就会被删除掉,所有的配置也就丢失了!

关闭内核log信息时间戳

仔细观察会发现Linux系统启动的时候每行信息前面都会打印出相应的时间,如下图所示:
log时间信息输出
从上图可以看出,每行log信息前面都有时间信息,调试时时间信息没什么用而且占据很长空间,可以通过配置内核将其关闭,配置路径如下:

-> Kernel hacking
-> printk and dmesg options
->Show timing information on printks //取消选中

如下图所示:
取消printk时间戳

编译测试

设备树修改好以后就可以编译了,因为只是修改了设备树,所以编译脚本直接用之前创建的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

当出现如下图所示log信息后就说明Linux启动运行成功:
Linux运行log信息

烧写系统镜像到EMMC中

上面已经测试过了Linux系统镜像和设备树,但是是通过tftp命令从网络上下载测试的,实际产品开发中最终是要将系统烧写到外部Flash中的,比如EMMC。本节来学习一下如何将uIamge和stm32mp157d-atk.dtb打包成ext4格式,然后通过STM32CubeProgrammer烧写至EMMC中,最终启动EMMC里的Linux系统。

系统镜像打包

首先就是将uImage和stm32mp157d-atk.dtb打包在一起,格式为ext4格式。当然了,也可以向这个ext4格式打包文件里面添加其他的内容,比如图片等,本节只用到uImage和stm32mp157d-atk.dtb。

打包过程在Ubuntu完成,首先新建一个名为“bootfs”的文件夹,然后将uImage和stm32mp157d-atk.dtb这两个文件放到bootfs文件夹下。

新建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行,进入到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”的磁盘,如下图所示:
bootfs.ext4磁盘

将系统镜像拷贝到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/

挂载成功以后就将上图中的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即可,命令如下:

sudo umount /mnt/bootfs

至此,uImage和stm32mp157d-atk.dtb就已经打包到了之前的bootfs.ext4中,稍后使用STM32CubeProgrammer软件将其烧写到EMMC里面。烧写之前最好在Windows下打开bootfs.ext4看一下,看看是否已经将uImage和stm32mp157d-atk.dtb打包进去,如下图所示:
bootfs.ext4

烧写到EMMC

接下来就是将上一小节打包好的ext4格式的bootfs.ext4烧写到开发板的EMMC里面,使
用STM32CubeProgrammer软件完成此操作。将bootfs.ext4拷贝到以前创建的images目录下,而后修改FlashLayout文件tf-s.tsv,在后面加入bootfs.ext4少些脚本,如下图所示:
修改后的FlashLayout
上图中第7行就是bootfs.ext4的烧写脚本,设置好以后就可以使用STM32CubeProgrammer烧写系统了,烧写完成以后设置拨码开关从EMMC启动,启动以后进入uboot的命令行,输入如下命令查看EMMC分区2里面是否正确烧写了uImage和stm32mp157d-atk.dtb:

ext4ls mmc 1:2

结果如下图所示:
EMMC分区2
从上图可以看出,此时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加载系统并运行,如下图所示:
EMMC启动系统
可以看出,将系统镜像烧写到EMMC里面还是很繁琐的,因此一般都是在产品最终开发完成,出厂的时候才会烧写到EMMC里面。在调试阶段都不会烧写到EMMC里面,而是在uboot里面使用tftp或nfs命令通过网络下载系统镜像并运行,这样当修改了系统以后只需要将系统镜像复制到tftp或nfs服务器目录即可,极大的简化开发方式

根文件系统缺失错误

Linux内核启动以后是需要根文件系统的,根文件系统存在哪里是由uboot的bootargs环境变量指定的,它会传递给Linux内核作为命令行(command line)参数。比如之前uboot移植中设置root=/dev/mmcblk2p3,也就是说根文件系统存储在/dev/mmcblk2p3中,也就是EMMC的分区 3中。 如果不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?这个问题是很常见的,在实际的工作中开发一个产品,这个产品的第一版硬件出来以后我们是没有对应的根文件系统可用的,必须要自己做根文件系统。 在没有根文件系统的情况下,Linux内核启动的时候就会有如下图所示的错误:
根文件系统缺失错误
在上图中的最后一行为:

end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

也就是提示内核崩溃,VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。解决方法就是制作根文件系统,并且设置uboot的bootargs环境变量,指定根文件系统所在的目录。

总结

完成Linux内核移植,需要完成以下步骤:

编译ST官方源码

首先进入已经获取得到的Linux源码,进入目录后解压,解压完成后进入源码目录并打补丁,而后生成默认配置文件.config,之后对这个文件继续打补丁,最后复制一份作为自己的默认配置文件并复制到自己的文件夹中。

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源码
tar -vxf linux-5.4.31.tar.xz
cd linux-5.4.31/ //进入Linux源码目录
for p in `ls -1 ../*.patch`; do patch -p1 < $p; done //打补丁
make ARCH=arm multi_v7_defconfig "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
cp .config ./arch/arm/configs/stm32mp1_atk_defconfig
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 //拷贝

如之前的TF-A以及U-Boot一样,需要配置ARCH和CROSS_COMPILE,然后写一个脚本来编译就好了。脚本名为“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

之后给予权限并运行脚本即可。

chmod 777 stm32mp157d_atk.sh //给予可执行权限,仅第一次
./stm32mp157d_atk.sh //运行编译脚本

之后需要修改网络驱动,换成自己的PHY芯片,将motorcomm.c和motorcomm_phy.h分别拷贝到Linux源码下的drivers/net/phy和include/linux目录下。拷贝完成以后修改drivers/net/phy/Makefile文件,加上下面这句:

obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o

然后修改drivers/net/phy/Kconfig文件,添加如下内容:

示例代码17.2.3.1 Kconfig添加的内容 
1 config MOTORCOMM_PHY 
2 tristate "Motorcomm PHYs" 
3 ---help--- 
4 Supports the YT8010, YT8510, YT8511, YT8512 PHYs.

最后通过“make menuconfig”配置使能YT8511驱动:

-> Device Drivers
-> Network device support (NETDEVICES [=y])
-> PHY Device support and infrastructure (PHYLIB [=y])
-> <*> Motorcomm PHYs //将 YT8511驱动编译进内核

添加开发板

arch/arm/boot/dts/目录下新建名为“stm32mp157d-atk.dtsi”的设备树头文件,然后将stm32mp15xx-edx.dtsi文件里面的内容全部复制到stm32mp157d-atk.dtsi里面。

再新建一个名为“stm32mp157d-atk.dts”的文件,将stm32mp157d-ed1.dts文件里面的内容都拷贝到 stm32mp157d-atk.dts里面。要将14行原来的“stm32mp15xx-edx.dtsi”头文件改为stm32mp15d-atk.dtsi。

修改stm32mp157d-atk.dtsi为如下内容:(添加电源节点信息)

// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
 * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
 * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
 */

#include "stm32mp157-m4-srm.dtsi" 
#include "stm32mp157-m4-srm-pinctrl.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/mfd/st,stpmic1.h>

/ {
	memory@c0000000 {
		device_type = "memory";
		reg = <0xC0000000 0x40000000>;
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		mcuram2: mcuram2@10000000 {
			compatible = "shared-dma-pool";
			reg = <0x10000000 0x40000>;
			no-map;
		};

		vdev0vring0: vdev0vring0@10040000 {
			compatible = "shared-dma-pool";
			reg = <0x10040000 0x1000>;
			no-map;
		};

		vdev0vring1: vdev0vring1@10041000 {
			compatible = "shared-dma-pool";
			reg = <0x10041000 0x1000>;
			no-map;
		};

		vdev0buffer: vdev0buffer@10042000 {
			compatible = "shared-dma-pool";
			reg = <0x10042000 0x4000>;
			no-map;
		};

		mcuram: mcuram@30000000 {
			compatible = "shared-dma-pool";
			reg = <0x30000000 0x40000>;
			no-map;
		};

		retram: retram@38000000 {
			compatible = "shared-dma-pool";
			reg = <0x38000000 0x10000>;
			no-map;
		};
	};

	vddcore: buck1 {
		compatible = "regulator-fixed";
		regulator-name = "vddcore";
		regulator-min-microvolt = <1200000>;
		regulator-max-microvolt = <1350000>;
		regulator-always-on;
		regulator-boot-on;
	};

	v3v3: regulator-3p3v {
		compatible = "regulator-fixed";
		regulator-name = "v3v3";
		regulator-min-microvolt = <3300000>;
		regulator-max-microvolt = <3300000>;
		regulator-always-on;
		regulator-boot-on;
	};
};

&cpu0{
	cpu-supply = <&vddcore>;
};

&crc1 {
	status = "okay";
};

&dma1 {
	sram = <&dma_pool>;
};

&dma2 {
	sram = <&dma_pool>;
};

&dts {
	status = "okay";
};

&ethernet0 {
	status = "okay";
	pinctrl-0 = <&ethernet0_rgmii_pins_a>;
	pinctrl-1 = <&ethernet0_rgmii_pins_sleep_a>;
	pinctrl-names = "default", "sleep";
	phy-mode = "rgmii-id";
	max-speed = <1000>;
	phy-handle = <&phy0>;

	mdio0 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "snps,dwmac-mdio";
		phy0: ethernet-phy@0 {
			reg = <0>;
		};
	};
};

&hash1 {
	status = "okay";
};

&ipcc {
	status = "okay";
};

&iwdg2 {
	timeout-sec = <32>;
	status = "okay";
};

&rng1 {
	status = "okay";
};

&rtc {
	status = "okay";
};

&sdmmc1 {
	pinctrl-names = "default", "opendrain", "sleep";
	pinctrl-0 = <&sdmmc1_b4_pins_a>;
	pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
	pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;
	broken-cd;
	st,neg-edge;
	bus-width = <4>;
	vmmc-supply = <&v3v3>;
	status = "okay";
};

&sdmmc2 {
	pinctrl-names = "default", "opendrain", "sleep";
	pinctrl-0 = <&sdmmc2_b4_pins_a>;
	pinctrl-1 = <&sdmmc2_b4_od_pins_a>;
	pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>;
	non-removable;
	st,neg-edge;
	bus-width = <8>;
	vmmc-supply = <&v3v3>;
	keep-power-in-suspend;
	status = "okay";
};

&sram {
	dma_pool: dma_pool@0 {
		reg = <0x50000 0x10000>;
		pool;
	};
};

&uart4 {
	pinctrl-names = "default", "sleep", "idle";
	pinctrl-0 = <&uart4_pins_a>;
	pinctrl-1 = <&uart4_sleep_pins_a>;
	pinctrl-2 = <&uart4_idle_pins_a>;
	pinctrl-3 = <&uart4_pins_a>;
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
};

最后打开arch/arm/boot/dts/Makefile,到“dtb-$(CONFIG_ARCH_STM32)”配置项,在此配置项中加入“stm32mp157d-atk.dtb”。

关闭内核模块验证:

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

关闭内核log时间戳,通过menuconfig关闭:

-> Kernel hacking
-> printk and dmesg options
->Show timing information on printks //取消选中

编译测试可以通过tftp来完成,在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

烧写到EMMC

可以创建一个ext4磁盘,然后将stm32mp157d-atk.dtb和uImage挂载上去。

示例代码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
sudo mkdir /mnt/bootfs
cd /home/zuozhongkai/linux/atk-mp1/linux/bootfs
sudo mount bootfs.ext4 /mnt/bootfs/
cd /home/zuozhongkai/linux/atk-mp1/linux/bootfs
sudo cp uImage stm32mp157d-atk.dtb /mnt/bootfs/
sudo umount /mnt/bootfs

然后修改之前的FlashLayout文件,添加bootfs.ext4,在设置bootcmd环境变量即可:

setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
saveenv
boot

总结之总结

这一部分移植主要是以使用为主,uboot要好好学一下详细启动,但是Linux内核过于大型,现在大概了解一下,会使用和大致了解启动过程,然后再学会了移植也就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1078925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【分享】小红书数据采集入excel表格

思路&#xff1a; 1. 打开小红书关键词页面 2. 循环指定次数&#xff0c;并鼠标往下滚 3. 获取元素列表&#xff0c;循环元素列表 4. 判断标题是否在list中&#xff0c;如果在就跳过&#xff0c;如果不在将标题存入list中 5. 点击元素&#xff0c;读取标题和内容&#xff…

LeetCode 1503. 所有蚂蚁掉下来前的最后一刻【脑筋急转弯】1618

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

2018架构真题案例(四十九)

某文件采用多级索引结构&#xff0c;磁盘大小4K字节&#xff0c;每个块号4字节&#xff0c;那么二级索引结果时&#xff0c;文件最大。 A、1024 B、1024*1024 C、2048*2048 D、4096*4096 答案&#xff1a;B 霍尔三维结构以时间堆、&#xff08;&#xff09;堆、知识堆组成…

vue踩坑

文章目录 1.error1 1.error1 在项目里面前端报这个错&#xff0c;有点蒙 确定了错误是在遍历数组中的图片部分 猜测可能是一开始的时候没有把photoList在form中写出来&#xff0c;form里面啥没有&#xff0c;导致渲染的时候有问题 所以以后在页面上渲染数据的都在data里…

网络-WebSocket

文章目录 前言一、WebSocket简介应用场景原理 二、使用心跳监测广播消息 三、群聊demo总结 前言 本文主要记录WebSocket的简单介绍和使用&#xff0c;完成群聊的demo 一、WebSocket简介 WebSocket是一种通信协议&#xff0c;它通过单个TCP连接提供全双工的通信通道。 它允许客…

【C#】标准WebService Soap1.1 兼容 ContentType: application/xml

一、问题描述 1.1 ESB平台要求 ContentType&#xff1a;application/xml Soap协议版本&#xff1a;1.1 1.2 提供的 WebService 接口 语言&#xff1a;C# 目标框架&#xff1a;.NetFramework 4.6.1 1.3 Postman 测试结果 HTTP Error 415.0 - Unsupported Media Type 服务器…

计算机竞赛YOLOv7 目标检测网络解读

文章目录 0 前言1 yolov7的整体结构2 关键点 - backbone关键点 - head3 训练4 使用效果5 最后 0 前言 世界变化太快&#xff0c;YOLOv6还没用熟YOLOv7就来了&#xff0c;如果有同学的毕设项目想用上最新的技术&#xff0c;不妨看看学长的这篇文章&#xff0c;学长带大家简单的…

如何为您的 Linux 服务器设置简单的 Grafana 云监控仪表板

Grafana 是一个开源可观察性平台,用于创建可视化数据集的仪表板。您可以使用它方便地监控服务器统计信息,例如 CPU 消耗、网络吞吐量和正常运行时间。 Grafana可以自托管,也可以通过官方Grafana Cloud实例作为 SaaS 解决方案进行访问。在本文中,您将了解如何设置 Grafana …

修炼k8s+flink+hdfs+dlink(四:k8s(一)概念)

一&#xff1a;概念 1. 概述 1.1 kubernetes对象. k8s对象包含俩个嵌套对象字段。 spec&#xff08;规约&#xff09;&#xff1a;期望状态 status&#xff08;状态&#xff09;&#xff1a;当前状态 当创建对象的时候&#xff0c;会按照spec的状态进行创建&#xff0c;如果…

scratch芝麻开门 2023年9月中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析

目录 scratch芝麻开门 一、题目要求 1、准备工作 2、功能实现 二、案例分析

操作系统学习笔记3-同步互斥问题

文章目录 1 同步与互斥逻辑图2、并发性异步性独立性3、临界资源临界区4、同步与互斥 1 同步与互斥逻辑图 2、并发性异步性独立性 3、临界资源临界区 4、同步与互斥

汽车一键启动点火开关按键一键启动按钮型号规格

汽车点火开关/移动管家一键启动按键/汽车改装引擎启动按钮型号&#xff1a;YD828溥款开关 一键启动按钮&#xff08;适用于配套启动主机使用或原车一键启动开关更换&#xff09; 1.适合配套专用板板安装 2.开孔器开孔安装 3.原车钥匙位安装 外观&#xff1a;黑色 按钮上有3种不…

【融合ChatGPT等AI模型】Python-GEE遥感云大数据分析、管理与可视化及多领域案例实践应用

目录 第一章 理论基础 第二章 开发环境搭建 第三章 遥感大数据处理基础与ChatGPT等AI模型交互 第四章 典型案例操作实践 第五章 输入输出及数据资产高效管理 第六章 云端数据论文出版级可视化 更多应用 随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近…

免费的ChatGPT与StableDiffusion AI绘画 二合一 附在线地址

ChatGPT与StableDiffusion 在线地址在文末 介绍 嘿&#xff0c;大家好&#xff01;今天我要给大家介绍一个非常酷炫的技术结合——ChatGPT与StableDiffusion的合作。听起来是不是很有趣&#xff1f;那么&#xff0c;让我们一起来看看这个组合到底能带给我们什么样的奇妙体验…

Go图片文件按照时间戳如何排序

涉及点包括 文件创建;时间控制器;自建封装包以及方法; 模板渲染;路由配置;不同的数据类型之间的转换拼接; 对于之前进行的文件上传操作,囊括单文件以及同名多文件和非同名多文件的编程方法,在生产中会遇到一个问题,如果上传的图片是同名的,那么在单文件上传的时候会将…

java 每种设计模式的作用,与应用场景

文章目录 前言java 每种设计模式的作用&#xff0c;与应用场景 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#xff0…

Android - Context

一、概念 Context 是应用程序和系统之间的桥梁&#xff0c;用于获取全局消息、访问系统资源、调用应用程序级的操作。一般直接调用 Context 的方法或者调用接口时传入Context。 Android应用模型是基于组件的应用设计模式&#xff0c;组件的运行要有一个完整的Android工程环境。…

Linux基础指令笔记大全

Linux基础指令笔记大全 1. ls 指令2. pwd命令3. cd 指令4. touch指令5. mkdir指令6. rmdir指令 && rm 指令7. man指令8.cp指令9. mv指令10. cat 指令11. more指令12. less指令13. head指令14. tail指令15. 时间相关的指令1. **在显示方面&#xff0c;使用者可以设定欲显…

华为云云耀云服务器L实例评测 | 实例使用教学之综合导览

华为云云耀云服务器L实例评测 &#xff5c; 实例使用教学之综合导览 实例使用教学实例场景体验实例性能评测实例评测使用介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀云…

上海亚商投顾:沪指探底回升 华为汽车概念股集体大涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日探底回升&#xff0c;早盘一度集体跌超1%&#xff0c;随后震荡回暖&#xff0c;深成指、创业板指…