LV05-03-Kernel-01-NXP-linux

本文主要是kernel——NXP官方固件的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.2的64位版本
VMware® Workstation 17 Pro 17.6.0 build-24238078
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04
linux内核 linux-4.19.71(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
nxp-imx/linux-imx/releases/tag/v4.19.71 NXP linux内核仓库tags中的v4.19.71
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)
Source Code https://elixir.bootlin.com/linux/latest/source linux kernel源码
https://elixir.bootlin.com/u-boot/latest/source uboot源码

一、linux内核

我们可以看到内核源码目录中大概会有这些文件:

image-20241102165526821
  • arch:主要包含和硬件体系结构相关的代码,如arm、x86、MIPS,PPC,每种CPU平台占一个相应的目录,例如我们使用的imx系列CPU就在 arch/arm/mach-imx 目录下,Linux内核目前已经支持30种左右的CPU体系结构。arch中的目录下存放的是各个平台以及各个平台的芯片对Linux内核进程调度、 内存管理、 中断等的支持,以及每个具体的SoC和电路板的板级支持代码。
  • block:在Linux中block表示块设备(以块(多个字节组成的整体,类似于扇区)为单位来整体访问),譬如说SD卡、Nand、硬盘等都是块设备,block目录下放的是一些Linux存储体系中关于块设备管理的代码。
  • crypto:这个文件夹下存放的是常用加密和散列算法(如md5、AES、 SHA等) ,还有一些压缩和CRC校验算法。
  • Documentation:内核各部分的文档描述。
  • drivers:设备驱动程序,里面列出了linux内核支持的所有硬件设备的驱动源代码,每个不同的驱动占用一个子目录,如char、 block、 net、 mtd、 i2c等。
  • fs:fs就是file system,里面包含Linux所支持的各种文件系统,如EXT、 FAT、 NTFS、 JFFS2等。
  • include:目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在 include/linux 子目录下,与cpu架构相关的头文件在include目录下对应的子目录中。
  • init:内核初始化代码,这个目录下的代码就是linux内核启动时初始化内核的代码。
  • ipc:ipc就是 inter process commuication ,进程间通信,该文件夹下都是linux进程间通信的代码。
  • kernel:kernel就是Linux内核,是Linux中最核心的部分,包括进程调度、定时器等,而和平台相关的一部分代码放在 arch/*/kernel 目录下。
  • lib:lib是库的意思,lib目录下存放的都是一些公用的有用的库函数,注意这里的库函数和C语言的库函数不一样的,因为在内核编程中是不能用C语言标准库函数的,所以需要使用lib中的库函数,除此之外与处理器结构相关的库函数代码被放在 arch/*/lib/ 目录下。
  • mm: 目录包含了所有独立于 cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等,而与具体硬件体系结构相关的内存管理代码位于 arch/*/mm 目录下,例如 arch/arm/mm/fault.c
  • net: 网络协议栈相关代码,net目录下实现各种常见的网络协议。
  • scripts:这个目录下全部是脚本文件,这些脚本文件不是linux内核工作时使用的,而是用了配置编译linux内核的。
  • security:内核安全模型相关的代码,例如最有名的SELINUX。
  • sound: ALSA、 OSS音频设备的驱动核心代码和常用设备驱动。
  • usr: 实现用于打包和压缩的cpio等。

提示:对于其他的未列出来的目录,暂时不用去理会。

二、linux内核源码下载

1. 版本选择

由于前面uboot升级到了2019.04,我们这里linux内核也选择一个同年份的,这里我选了4.19.71,主要是它的版本大于4.4支持设备树插件,也有利于后续的学习。

这个版本对于仓库来说还有有些老了,tag地址比较好找:Release v4.19.71 · nxp-imx/linux-imx (github.com),好像还有个类似分支的地址是nxp-imx/linux-imx at v4.19.71 (github.com)

image-20241102164724133可以看到提交记录是:e7d2672c66e4d3675570369bf20856296da312c4。我们点开这个tag就能看到所在的分支:

image-20241102164811762

它所在分支地址:nxp-imx/linux-imx at git.kernel.org/linux-stable/linux-4.19.y (github.com),不过有tag的话我们一般还是使用tag来下载源码。

2. 源码下载

我们通过以下命令下载源码:

1
2
git clone -b v4.19.71 --depth=1 https://github.com/nxp-imx/linux-imx.git
git clone -b v4.19.71 --depth=1 git@github.com:nxp-imx/linux-imx.git

这个仓库比较大,我就没放到自己的gitee上了,只是存了一些修改的文件。

三、编译源码

1. 安装依赖库

貌似前面uboot已经装过了,通用的,这里就不用了。野火的教程中需要安装这些:

1
sudo apt-get install lzop libncurses5-dev

这个lzop要装,不然会报错:

image-20241102171357088

2. 编译源码

2.1 清理工程文件

1
2
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean

这里在回顾一下吧:

clean命令主要清除的是编译过程中生成的可执行文件以及配置文件。这些文件是具体编译任务的产物,例如.exe文件、.o文件等。清除这些文件有助于保持构建环境的干净,避免可能因残留文件导致的问题。然而,make clean并不会清除其他一些可能存在的文件,如日志文件、中间编译结果等,这使得它在彻底性上有所不足。

make distclean命令则更为彻底。它不仅清除可执行文件和配置文件,还会清除所有由make命令生成的文件。这包括了编译过程中产生的中间文件、日志文件、缓存文件等。通过执行make distclean,我们能够确保构建环境的完全清洁,为后续的构建过程提供一个干净、无残留的环境。然而,这也意味着清除了一些可能需要保留的文件,比如版本控制系统的缓存文件等。

不过吧,由于有的时候我们在图形界面配置完linux内核,可能会忘记保存config文件,这个时候用distclean会把配置文件一起删了,这样其实是没有必要的,所有后面我基本用的都是clean命令,需要重新配置的话再用distclean。

2.2 配置工程

1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig

2.3 编译源码

1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

这个命令中all表示编译所有内容,包括设备树和内核镜像。不出意外的话编译完是这样的:

image-20241102171750221

Linux 内核编译完成以后会在 arch/arm/boot 目录下生成 zImage 镜像文件,如果使用设备树的话还会在 arch/arm/boot/dts 目录下开发板对应的.dtb(设备树)文件,比如 imx6ull-14x14-evk.dtb就是 NXP 官方的 I.MX6ULL EVK 开发板对应的设备树文件。至此我们得到两个文件:

(1)Linux 内核镜像文件: zImage。

(2)NXP 官方 I.MX6ULL EVK 开发板对应的设备树文件: imx6ull-14x14-evk.dtb。

3. uImage与zImage

在之前的一些老一点的版本内核中,我们有可能会见到uImage,这里我们可以看到编译出来的事zImage,他们有什么区别?我们先来看一下成果物目录,我们根据编译信息,可以知道内核相关的成果物是在arch/arm/boot这个目录下的:

image-20241117140433775

内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,从图中可以看出Image大约为18M,而zImage大概是8M。

linux内核经过编译后会生成一个elf格式的可执行程序,叫vmlinux或vmlinuz,这个就是原始的未经任何处理加工的原版内核elf文件:

image-20241117141034327

嵌入式系统部署时烧录的一般不是这个vmlinuz/vmlinux,而是要用objcopy工具去制作成烧录镜像格式,经过制作加工后的烧录镜像文件就叫Image。原则上Image就可以直接被烧录到Flash上进行启动执行(类似于u-boot.bin),但是实际上并不是这么简单。linux的大佬们觉得Image还是太大了!!所以对Image进行了压缩,并且在image压缩后的文件的前端附加了一部分解压缩代码,构成了一个压缩格式的镜像就叫zImage。运行的时候,通过zImage镜像头部的解压缩代码进行自解压,然后执行解压出来的内核镜像。

那么uImage又是什么的?它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。

有些uboot是支持zImage启动的,有些则不支持。但是所有的uboot肯定都支持uImage启动(常用方式,有更多的优点)。

注意:uImage不关linux内核的事,linux内核只管生成zImage即可,这个加工过程其实就是在zImage前面加上64字节的uImage的头信息即可。

4. 只编译内核

linux内核源码可以编译内核和设备树,这两个都有单独的命令可以进行编译,单独编译内核我们可以执行:

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j16
image-20241117142509551

会发现这样只会生成内核的镜像。

5. 编译设备树

编译设备树也有单独的命令:

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs -j16
image-20241117142741537

发现没有生成内核镜像文件,但是有一堆的设备树文件了。那能不能单独编译我指定的设备树文件?

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx6ull-14x14-evk.dtb -j16
image-20241117143010638

发现最后就只生成了这一个设备树文件。

四、内核启动测试

1. tftp环境搭建

这个主要是在ubuntu搭建tftp环境,可以看这个《01嵌入式开发/02IMX6ULL平台/LV02-开发环境/LV02-03-网络环境-02-TFTP环境搭建.md》

2. 拷文件到服务器目录

1
2
3
cd ~/7Linux/imx6ull-kernel 
cp -f arch/arm/boot/zImage ~/3tftp/
cp -f arch/arm/boot/dts/imx6ull-14x14-evk.dtb ~/3tftp/
image-20241102183137319

3. 启动测试

我们使用前面自己移植的uboot,要保证网口可以正常使用:

image-20241102183326480

启动开发板后进入命令模式,然后输入以下命令下载内核镜像和设备树到开发板指定的地址:

1
2
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
image-20241102183417930

最后我们启动内核:

1
bootz 80800000 - 83000000
image-20241102183536564

有这些出现表示内核已经成功启动了。但是最后一直处于这个状态:

image-20241102184349887

我们后面再说为什么。

4. 一个问题

我们上面启动后,最后一直提示在打开网口,为什么?

Linux 内核启动以后是需要根文件系统的,根文件系统存在哪里是由 uboot 的 bootargs 环境变量指定,bootargs 会传递给 Linux 内核作为命令行参数 。这里我之前配置了uboot的ip以及服务器参数,默认情况下,会从网络挂载根文件系统:

image-20241102184518183

所以之前启动后,由于linux还未适配我们开发板的网口,驱动还未移植,所以网络不通,就没法挂载就会一直不断尝试。那如果我们不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?这个问题是很常见的,我们在实际的工作中开发一个产品,这个产品的第一版硬件出来以后我们是没有对应的根文件系统可用的,必须要自己做根文件系统。在构建出对应的根文件系统之前 Linux 内核是没有根文件系统可用的,此时 Linux 内核启动以后会出现什么问题呢?我们在uboot中设置:

1
setenv bootargs 'console=ttymxc0,115200' # 设置 bootargs
image-20241102184701087

然后按照刚才的命令,启动一下linux内核,看一下会有什么现象:

image-20241102184807074

会发现最后报了一句:

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

也就是提示内核崩溃,因为 VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。即使根文件系统目录存在,如果根文件系统目录里面是空的依旧会提示内核崩溃。这个就是根文件系统缺失导致的内核崩溃,但是内核是启动了的,只是根文件系统不存在而已。