linux内核移植研究学习
1.内核移植的基本内容
内核移植包括 : 裁剪 + 配置 + 适配。
提问:
为什么移植一定包含裁剪?
1.硬件资源有限:嵌入式设备(ARM 开发板、IoT 芯片等)Flash、RAM 很小,原版内核体积太大,根本跑不起来。
2.只保留需要的驱动和功能: 开发板子只有特定的串口、网卡、Flash,不需要显卡、声卡、PCIe 等大量无关驱动。
3.提高启动速度、减少漏洞面:裁剪后内核更小、启动更快、更稳定
内核移植的典型完整流程?
1.获取内核源码: 选择对应平台的默认配置(defconfig)
2.裁剪与配置内核: 去掉不需要的文件系统、驱动、网络协议 ; 开启必要的驱动(UART、MMC、LCD、NAND 等); 配置内存大小、启动参数等 ;修改板级相关代码
3.适配硬件:时钟、引脚、中断、内存映射等
4.编译内核(uImage/zImage/Image)
5.烧录到开发板测试、调试
注意:我们桌面linux一般不需要裁剪,但是嵌入式linux必须裁剪。否则内核太大、功能冗余、无法正常启动。
uboot移植,内核移植,以及文件系统构建三者的关系?

2.内核编译过程
1.首先把内核放入ubantu中,解压

2.解压后,得到下面的内容,我们不需要压缩包,压缩包通过rm删除

3.然后我们需要对内核进行编译,为了方便,我们把内核编译的四个命令放到一个脚本里,这样执行这个脚本就相当于输入了这四个命令,不用每次编译都重新输入命令。
4.正式编译
通过执行脚本进行编译。

过程总结:
下载NXP官方维护的linux内核版本,进行改造,直接使用原有的内核版本修改难度大。因为里面大部分驱动是没有的, 然后进解压,编译,试运行。
知识补充:
脚本命令解析
vi build.sh //创建名为build的脚本,并进入脚本
chmod +x build.sh //给予脚本执行权限,一般都是给777,即chmod 777 build.sh
./build.sh //执行这个脚本,即执行这个脚本的全部命令
脚本内容
#!/bin/bash是一个shell解释器,可以暂且理解为,这个是用来执行脚本的默认工具

内核编译命令解析
视频中的编译命令是这样写的
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean # 清理
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig # 默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig # 手动调整配置(可选)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j # 编译
但其实也可以这样写,接下来以下面的版本进行解析
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make distclean // 进行编译文件清除
make imx_v7_defconfig // 使用默认配置文件
make menuconfig //调出配置界面(可选),不操作,按两个exit可退出
make all -j4 编译
export是脚本中的一个专用命令,是用于设置环境变量的
export ARCH=arm 表示编译目标是arm架构,并不是默认的x86架构
export CROSS_COMPILE=arm-linux-gnueabihf- 表示使用arm-linux-gnueabihf-开头的交叉工具链进行编译
export让后面的环境变量被子进程继承,也就是被每个make继承。
内核默认配置文件一般在哪个文件夹
即make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- linux_v7_defconfig中的linux_v7_defconfig在哪里。
解压内核后,在arch/arm/configs里面,里面文件比较多,可以直接搜索。

编译报错处理(重要)
报错如下

报错分析:
bin/sh : 1 : lozp:not found //原因
recipe for target 'arch/arm/boot/compressed/piggy.lzo' failed // 结果
.....省略.....
recipe for target 'arch/arn/boot/compressed/vmlinux' failed
注意我们前面在编译脚本的地方提到的。
#!/bin/bash是一个shell解释器,可以暂且理解为,这个是用来执行脚本的默认工具
第一行:
/bin/sh :执行命令的 shell 程序
1 :shell 正在执行的命令行编号,通常表示第 1 行
lzop :真正找不到的命令
not found :系统在 PATH 里没找到 lzop 这个可执行程序
第二行:
这个是make 内核的过程中的一个经典的报错形式。意思是Makefile 里用于生成 XXXX 这个目标的命令执行失败了。值得注意的是recipe for target "路径" failed 这是一个结果性的报错。真的的原因要从上面找。这里发现是 XXX not found , 这是一个缺工具的报错,一般安装工具就可以处理了,这里的处理方法就是。进行工具的安装。执行命令如下。另外,值得注意的是,这一个报错,其实导致了两个报错,我们可以看到,有两个 recipe for target XXX faild
sudo apt-get install lzop
处理结果:
当安装工具后,执行脚本,报错消失,得到内核镜像文件zImage 以及文件 piggy.lzo

知识补充:
缺XXX报错提示总结:
缺库,缺工具,缺依赖并不是一个概念。前面的 lzop,它更准确地说是 缺工具/缺软件,不是缺库。因为内核编译时要执行 lzop 这个压缩命令来生成压缩镜像,如果系统找不到这个命令,就会报错。所以,缺 lzop 不是缺库,是缺工具。缺 libssl.so 才是缺库。缺 openssl/ssl.h 是缺头文件,通常要装开发
| 类型 | 缺的是什么 | 典型报错 | 例子 |
|---|---|---|---|
| 缺工具 / 软件 | 一个可执行命令 | xxx: command not found、xxx: not found | lzop、bc、flex、bison、make、gcc |
| 缺头文件 | .h 文件 | fatal error: xxx.h: No such file or directory | openssl/ssl.h、ncurses.h |
| 缺库 | .so 或 .a 库文件 | cannot find -lxxx、undefined reference to xxx | libssl、libz、libncurses |
包。
sudo apt-get install XXX 命令解释:
sudo 是以超级用户,也就是root的权限执行命令,因为安装软件需要修改系统级别的目录
apt-get是一个包管理工具,这里记为一个负责软件安装,升级,删除的一个工具
install 在这里就是apt-get的一个子命令,没有别的含义
ubantu如何下载软件的?
ubantu可以连接一个官方的软件源,可以理解为windows的应用商店。我们输入命令后,ubantu上网去这个软件源中找。并且会负责,下载,安装,配置的全部内容,我们可以直接使用。但是,但是,但是,我们是幼配置镜像源的,因为ubantu是默认连接国外服务器的,我们要配置镜像源为国内服务器,这个是安装不上软件的原因/
3.内核镜像与设备树传入开发板
内核和设备树都编译准备好之后,拷贝到tftp文件夹,通过tftp传给开发板。主要操作就是在串口执行tftp命令,进行传输,传输完成后,使用bootz命令进行内核启动。总结一下就是:内核镜像 zImage 和设备树 .dtb 可以通过 TFTP 下载到开发板内存中,然后由 U-Boot 启动。
知识补充:
tftp是什么,怎么用,
TFTP 是 Trivial File Transfer Protocol,简单文件传输协议。在这个场景里,它的作用很单纯:从 Ubuntu 主机的 tftpboot 目录里,把 zImage 和 .dtb 下载到开发板 DDR 内存的指定地址。
并且,这里 tftp 不是把文件永久烧写进 EMMC,而是把文件临时下载到 DDR 里,然后 bootz 从 DDR 启动内核。教程中也是这样设置 bootcmd 的:先用 TFTP 下载 zImage 和设备树,再执行 bootz 启动。
注意两个点:暂时放入ddr,不是永久烧写进EMMC
为什么这里不用nfs传输,看起来一样啊?
为什么这里常用 TFTP,而不是 NFS?核心原因是:此时还在 U-Boot 阶段,只需要下载两个文件,不需要挂载一个完整文件系统。TFTP 适合这种场景:一次性下载单个文件,比如zImage ,以及,dtb。NFS 更适合这种场景:长期挂载一个目录作为根文件系统。
tftp哪里来的?
U-Boot 自带了 TFTP 客户端命令/功能。只要你烧录运行的 U-Boot 编译进了网络功能和 tftp 命令,就可以在 U-Boot 命令行里使用 tftp。我们需要准备的是咋ubantu上在安装tftp服务端。
但是使用tftp也有一个大前提,首先就是uboot编译的时候打开了网络命令,比如tftp ,ping等。第二个就是,uboot已经适配了网卡驱动,并且后面的网络硬件配置,环境变量配置都正确,才可以正常使用
内核来源?
1.我们学习的是修改NXP内核的的方法,正点原子从NXP买了芯片,然后做了自己的开发板,把自己的开发板添加到NXP提供的内核中去
2.从内核官网,直接下载内核,进行修改,难度是非常之大的。因为会少很多复杂的驱动,这些驱动时半导体公司做的比如NXP
报错处理:
报错显示:
make: *** No rule to make target 'dtbs'. Stop.

原因:
编译顶层设备树的时候需要在顶层的makefile进行编译。Linux 内核源码里有很多个 Makefile,但编译时通常要从“内核源码顶层目录”的 Makefile 入口开始。所以后面回到顶层目录,重新执行设备树编译就OK了

知识补充:
Linux 内核编译的时候,顶层 Makefile 起什么作用?
Linux 内核源码里有很多 Makefile,但真正的编译入口是源码根目录下的顶层 Makefile。它负责确定架构、交叉编译器、编译目标、配置文件,并通过 Kbuild 体系递归调用各级子目录的 Makefile,最终生成 vmlinux、zImage、.dtb、.ko 等产物。
我们配置ARCH 和 CROSS_COMPILE是为了什么?
嵌入式 Linux 是交叉编译,Ubuntu 主机是 x86,但目标板是 ARM,所以要告诉 Makefile:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
ARCH:指定目标架构,比如 arm。CROSS_COMPILE:指定交叉编译工具链前缀
defconfig 和 .configs是什么文件?
xxx_defconfig 是默认配置模板;
.config 是当前真正生效的内核配置文件;
make menuconfig 修改的是 .config;
最终编译内核时,Kbuild 依据 .config 决定哪些模块编译进内核,哪些编译成模块,哪些不编译。
总结一下就是.configs是真正在用的文件。
Kconfig 和 Makefile 的区别?
Kconfig:负责提供配置选项,比如 menuconfig 里看到的菜单;
.config:保存用户最终选择;
Makefile/Kbuild:根据 .config 决定具体编译哪些文件。
比如某个驱动是这样写的:
obj-$(CONFIG_GPIO_LED) += gpio_led.o
如果 .config 里有:
CONFIG_GPIO_LED=y //那它会编译进内核。
CONFIG_GPIO_LED=m //那它会编译成 .ko 模块。
//如果没有配置,就不编译。
built-in.o 和 vmlinuxs是什么?
各个目录下的 .o 文件会先被打包/链接成 built-in.o;
然后多个 built-in.o 再参与链接,最终生成 vmlinux;
vmlinux 是未压缩的内核 ELF 文件;
zImage 是压缩后的 ARM Linux 内核镜像。
设备树编译如何编译?
设备树不是直接手动编译某个 dts 文件,而是通过内核顶层 Makefile 的 make dtbs 目标触发 DTC 编译。
linux内核的启动流程?
U-Boot → zImage 解压 → stext → __mmap_switched → start_kernel → rest_init → kernel_init → 挂载根文件系统 → 执行 init
U-Boot 如何到内核?
1. 把 zImage 加载到 DDR
2. 把 dtb 加载到 DDR
3. 设置 bootargs
4. 执行 bootz
5. 跳转到内核入口
4.把自己的开发板添加到内核
这部分本质上是在 NXP 官方 Linux 内核源码里,给我们自己的开发板,也就是给正点原子 I.MX6U-ALPHA EMMC 版开发板建立一套“自己的板级入口”。这一步并不是从零写内核,而是基于 NXP 官方 I.MX6ULL EVK 开发板做拷贝、改名、适配。
而具体要做的可以总结为以下三个内容:
1. 添加自己的 defconfig
2. 添加自己的 dts 设备树
3. 修改 dts 目录下的 Makefile,让设备树能被编译成 dtb
1.首先实验一下NXP官方的内核和设备树,编译后,使用bootz命令,让uboot启动内核和设备树是否会报错。
这里看到没有问题,下面唯一的一个报错,其原因是没有根文件系统。如果这里不能启动,事情就可能变得麻烦了,一般都是可以启动的。

也就是deconfig文件,这里再复习一下,deconfig文件,是默认配置文件,而.config文件是真正使用的配置文件,我们使用的meunconfig修改的 ,就是这个.config文件。以上两个文件如何得到呢,其实和uboot那部分一样,直接复制就可以了。
首先复制配置文件deconfig:
将 arch/arm/configs 目录下的 imx_v7_mfg_defconfig 重新复制一份,命名为 imx_alientek_e
mmc_defconfig,命令如下:
cd arch/arm/configs
cp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig
然后打开这个配置文件,把"CONFIG_ARCH_MULTI_V6=y"这一行变为"#CONFIG_ARCH_MULTI_V6=y"也就是注释掉,因为imx6ull是armv7架构的,v6架构的我们不要。
make imx_alientek_emmc_defconfig
然后,复制设备树文件dts:
cd arch/arm/boot/dts
cp imx6ull-14x14-evk.dts imx6ull-alientek-emmc.dts
这里复制哪个设备树文件其实有讲究,主要是按照最相似的原子去复制,imax6ull是板子的主控芯片型号,14x14是芯片封装尺寸的意思。这个好理解,一般尺寸大,引脚多。
3.修改顶层makefile文件


4.写一个脚本,编译一下
我们自己写一个脚本进行编译,这里也不是自己手写,而是找到刚才我们复制的哪个deconfig文件,稍微修改一下他的编译脚本。

编译脚本如下imx6ull_alientek_emmc.sh(这个名字可以自己修改,是复制来的)的内容如下
#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_emmc_defconfig //主要改这里,使用我们自己的配置文件,dts文件已经通过修改顶层makefile告知内核了
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16//这里调用顶层makefile
第 2 行,清理工程。
第 3 行,使用默认配置文件 imx_alientek_emmc_defconfig 来配置 Linux 内核。
第 4 行,打开 Linux 的图形配置界面,如果不需要每次都打开图形配置界面可以删除此行。
第 5 行,编译 Linux。 //使用顶层makefilej进行编译


提问
1.make imx_alientek_emmc_defconfig生成什么文件?
它不是在编译 zImage,也不是在生成 .dtb,而是告诉内核构建系统:我要按照 imx_alientek_emmc_defconfig 这份默认配置,生成当前内核要使用的 .config 文件。所以是生成deconfig文件
2..config 决定什么?
.config 里面保存了大量配置项,比如:
CONFIG_EXT4_FS=y
CONFIG_NET=y
CONFIG_MMC=y
CONFIG_USB_SUPPORT=y
CONFIG_GPIO_SYSFS=y
它决定:
哪些功能编进内核
哪些功能编成模块
哪些功能不编译
使用哪个 CPU 架构
打开哪些驱动
支持哪些文件系统
支持哪些网络功能
3. make deconfig ,make menuconfig ,make -j16 三者关系
defconfig:菜单模板
make xxx_defconfig:按这个菜单生成今天的点菜单 .config
make menuconfig:手动微调今天的点菜单
make -j16:厨师真正开始做菜
zImage / dtb / ko:最后做出来的菜
4.如何使用的?
通过我们写一个编译脚本,然后make这个脚背,进行使用。里面使用了deconfig文件,也使用了顶层的makefile,也就是是我们前面的第四步
5.CPU主频和网络驱动修改:
虽然前面内核和uboot都已经做出过调整,但这还不够。因为从 NXP 官方 EVK 板复制过来的配置,和我们自己板子的硬件并不完全一致,所以 37.4 要继续修改 CPU 主频、EMMC、网络驱动。
这部分可以理解为,在前面的内容中, 已经让内核“有了自己的板子入口”,这部分是继续把板子的关键硬件参数调正确。以确保正常运行。
需要注意的是: 在进行这部分修改之前,需要先进行根文件系统的移植。
1.CPU调频策略修改
这个比较简单,直接通过menuconfig就可以,选则不同的调频策略就OK。但是需要注意的一点menuconfig修改的是当前的配置文件.config。不会修改板级配置文件deconfig。
这个修改是为了让内核知道cpu可以跑哪些频率。CPU 主频不是随便写一个数字就行。Linux 里有一个 CPUFreq 调频框架,它会根据设备树和内核配置决定 CPU 可以在哪些频率之间切换。我们修改的内容主要包括两个部分:1.CPU可以跑哪些频率。2.CPU的调频策略
cpu主频修改 = 修改配置文件 + 修改设备树文件。
查看CPU信息
/sys/bus/cpu/devices/cpu0/cpufreq

打印主频信息,这里可以看到CPU目前的运行频率是 792Mhz
cat cpuinfo_cur_freq
以下是其他文件代表的意义。
cpuinfo_cur_freq:当前cpu工作频率,从CPU寄存器读取到的工作频率。
cpuinfo_max_freq:处理器所能运行的最高工作频率(单位:KHz)。
cpuinfo_min_freq:处理器所能运行的最低工作频率(单位:KHz)。
cpuinfo_transition_latency:处理器切换频率所需要的时间(单位:ns)。
scaling_available_frequencies:处理器支持的主频率列表(单位:KHz)。
scaling_available_governors:当前内核中支持的所有 governor(调频)类型。
使用meauconfig修改调频策略,需要注意的是所有调频策略是默认都使能的,但是哪个是默认的调频策略是需要设置的,一般推荐ondemand。

2.增加CPU的支持频率

3.EMMC 驱动修改
修改CPU主频,需要先装好根文件系统。而根文件系统后面往往就在 EMMC 上。而EMMC 修改 = 修改 usdhc2 设备树节点。在下面的修改中,核心板 EMMC 工作电压是 3.3V,所以禁止切换到1.8伏
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc2_8bit>;
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
bus-width = <8>; //EMMC 使用 8 位数据线
non-removable; //用板载 EMMC ,不用SD卡
no-1-8-v; //禁止切换到1.8伏
status = "okay"; //启用这个控制器
};
4.网络驱动修改:
网络这部分最重要。因为后面做驱动开发时,经常要用:比如TFTP 下载 zImage/dtb, NFS 挂载根文件系统 , scp/ftp 传驱动和应用 ,ping 测试网络、如果 Linux 网卡驱动没调好,后面的实验会很难受。
1.硬件结构
一个完整的网口由以下四个部分组成
IMX6ULL 内部 MAC → RMII 接口 → 外部 PHY → 网络变压器/RJ45,芯片手册中,也表明了MAC的位置如下

所以 Linux 网络要起来,至少要满足这几部分都对:所以这部分不是“写一个新的网卡驱动”,而是 设备树 + 少量 FEC 驱动源码 + menuconfig 配置 三者一起改。
1. MAC 驱动要对
对应 Linux 里的 freescale/fec_main.c
2. PHY 地址要对
通过 MDIO 总线访问 PHY,地址错了就找不到 PHY
3. PHY 复位脚要对
复位 GPIO 不对,PHY 可能一直没正常启动
4. 引脚复用和电气属性要对
RMII 数据线、时钟线、复位线都要正确配置
5. PHY 驱动要使能
比如 LAN8720A 需要 SMSC PHY 驱动
2.为什么需要修改
当然是硬件资源不同+ 硬件结构不同。更具体的来说就是:
1. PHY 芯片型号不同
2. PHY 地址不同
3. PHY 复位 GPIO 不同
4. ENET 时钟引脚配置不同
5. 底板版本不同,网络方案也不同
3.主要改哪些文件?
1.描述板子的网络硬件:PHY 地址、复位 GPIO
2.NXP/Freescale FEC 网卡 MAC 驱动源码。
3.使能对应 PHY 驱动
arch/arm/boot/dts/imx6ull-alientek-emmc.dts
drivers/net/ethernet/freescale/fec_main.c
.config / imx_alientek_emmc_defconfig
4.修改复位引脚以及网络时钟引脚驱动
这个部分本质上是修改外部 PHY 芯片的板级适配。对于IMAX6ULL,ENET1 的复位脚 ENET1_RST 接到 I.MX6ULL 的 SNVS_TAMPER7,ENET2 的复位脚 ENET2_RST 接到 SNVS_TAMPER8。
第一点:根据手册
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08
那么这两个引脚复用为GPIO功能后,就是GPIO5_IO07以及GPIO5_IO08。
第二点:SNVS_TAMPER7复用为GPIO给PHY用之前,需要先从原来的工作位置释放出来也就是从SPI4释放出来。所以在设备树中,找到spi4的节点,给他频闭掉


第三点:前面完成了引脚释放,这里要完成,引脚的重新管理。
GPIO5_IO07给ENET1

GPIO5_IO08给ENET2

5.设备树节点修改
这里主要修改的是,设备树对网卡的描述。这里的描述改成imx6ull的网卡。其中fec1 对应 ENET1,fec2 对应 ENET2
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_enet1_reset>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2
&pinctrl_enet2_reset>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0>;
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
};
};
};
各个部分代表的意思,这里主要修改的就是pinctrl-0。这部分是硬件中,引脚使用相关的描述。
pinctrl-0
指定 ENET 使用哪些引脚配置。
phy-mode = "rmii"
表示 MAC 和 PHY 之间用 RMII 接口。
phy-handle
指向具体 PHY 芯片节点。
phy-reset-gpios
指定 PHY 的复位 GPIO。
phy-reset-duration
指定复位持续时间,文档中常用 200ms。
mdio
描述挂在 MDIO 总线上的 PHY。
reg
表示 PHY 地址。
5.PHY 地址修改
MAC 通过 MDIO 总线访问 PHY。MDIO 总线上可以挂多个 PHY,每个 PHY 有自己的地址。原来的地址是NXP官方自己的,这里我们改成我们板子上的PHY地址,注意,PHY已经在封装的芯片外部了,不是芯片内部。从1和2改成0和1

6.修改驱动源码
主要是进行寄存器的初始值设置

提问:
修改PHY地址的重要性?
PHY 地址写错,会导致Linux FEC 驱动找不到 PHY,进而导致eth0/eth1 起不来,板子的所有网络功能相关的命令都无法使用,比如ping ,等等
PHY复位修改重要性?
PHY 芯片上电后,需要复位脚按正确时序释放,否则 PHY 可能不会正常工作。
修改设备树,修改驱动源码,修改meauconfig的原因?
设备树描述硬件,驱动源码处理特殊时序/寄存器,menuconfig 使能对应驱动
总结:
内核移植流程

更多推荐




所有评论(0)