第三章:Linux内核编译——获取源码、配置与部署
好,咱们进入正题。内核编译这件事,说白了就是给Zynq的ARM核准备一个操作系统内核。很多新手觉得这步很神秘,其实拆开来看,就三步:拿源码、配选项、编译部署。我在Zynq项目上折腾过不下几十次内核编译,踩过的坑能写满一页A4纸。今天我把这些经验都抖出来。
3.1 获取Linux内核源码
源码从哪来?两个主流选择:
- Xilinx官方维护的版本:在GitHub上搜"xilinx-linux",这是最稳妥的选择。我习惯用这个,因为它对Zynq外设的支持最全。
- 主线Linux内核:从kernel.org下载。好处是更新快,但你需要自己打Xilinx的补丁。
我个人建议用Xilinx版本。为什么?你想想看,Zynq的DMA、FPGA桥接这些外设,主线内核不一定有现成的驱动。Xilinx版本已经帮你配好了。
获取方式很简单:
git clone https://github.com/Xilinx/linux-xlnx.git
cd linux-xlnx
git checkout xilinx-v2023.1 # 选一个稳定版本
嗯,这里要注意版本号。我遇到过有人直接clone master分支,结果编译到一半报错。选一个带"xilinx-v"前缀的tag,这些是经过测试的。
3.2 配置内核选项
源码拿到了,接下来是配置。这一步最考验经验。配置不对,轻则功能缺失,重则系统起不来。
3.2.1 基础配置方法
Zynq有默认配置模板,省了不少事:
make ARCH=arm xilinx_zynq_defconfig
这条命令会生成一个基础配置。但它只是"能跑"的水平。你要做的是在此基础上裁剪和添加。
我习惯用menuconfig做微调:
make ARCH=arm menuconfig
界面里按"/"可以搜索选项。比如你要找"SPI"驱动,按"/"输入SPI,它会告诉你路径在哪。
make savedefconfig保存一份。之后每次改配置,都跟这份对比,避免误删关键选项。
3.2.2 必须开启的选项
根据我的项目经验,以下选项是Zynq平台的标配:
| 功能 | 配置路径 | 说明 |
|---|---|---|
| UART | Device Drivers → Character devices → Serial drivers → Xilinx UART | 控制台输出,不开你连log都看不到 |
| SD/MMC | Device Drivers → MMC/SD/SDIO card support → Xilinx SDHCI | 从SD卡启动系统必备 |
| FPGA桥接 | Device Drivers → FPGA Configuration Framework → Xilinx FPGA manager | 动态加载FPGA bitstream |
| DMA引擎 | Device Drivers → DMA Engine support → Xilinx DMA engines | 高速数据传输,不开性能掉一半 |
3.2.3 设备树配置
配置完内核选项,别忘了设备树。Zynq的设备树文件在arch/arm/boot/dts/zynq-*.dts。你需要根据你的硬件修改:
// 修改内存大小
memory {
device_type = "memory";
reg = <0x0 0x40000000>; // 1GB内存
};
// 添加自定义外设
&amba {
my_ip: my_ip@43c00000 {
compatible = "xlnx,my-ip-1.0";
reg = <0x43c00000 0x10000>;
interrupt-parent = <&intc>;
interrupts = <0 59 4>;
};
};
设备树这东西,说白了就是告诉内核"你的硬件长什么样"。地址、中断号、寄存器大小,一个都不能错。我习惯对照Vivado的地址映射表来写,这样最保险。
3.3 编译与部署
配置搞定,开始编译。这一步最吃机器性能,也最考验耐心。
3.3.1 编译命令
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make -j4 UIMAGE_LOADADDR=0x8000 uImage
make -j4 modules
make -j4 dtbs
解释一下:
-j4:用4个线程并行编译。我一般设成CPU核心数+1。UIMAGE_LOADADDR=0x8000:Zynq的加载地址,固定值别改。uImage:生成带uboot头的内核镜像。modules:编译内核模块,后面要装到文件系统里。dtbs:编译设备树,生成.dtb文件。
第一次编译大概要10-15分钟。如果只改了几个选项,用make -j4就行,它会增量编译,快很多。
常见错误及解决:
- 报"arm-linux-gnueabihf-gcc: not found" → 没装交叉编译器。装一下:
sudo apt install gcc-arm-linux-gnueabihf - 报"mkimage: command not found" → 缺uboot工具。装:
sudo apt install u-boot-tools - 报"undefined reference to xxx" → 配置有冲突。试试
make clean后重新配置。
3.3.2 部署到Zynq板
编译完会生成三个关键文件:
arch/arm/boot/uImage:内核镜像arch/arm/boot/dts/zynq-你的板子.dtb:设备树arch/arm/boot/zImage:另一种格式的内核,有时也用
部署方式取决于你的启动介质:
SD卡启动:
# 把SD卡插到电脑,挂载到/media/boot
cp arch/arm/boot/uImage /media/boot/
cp arch/arm/boot/dts/zynq-你的板子.dtb /media/boot/
sync
umount /media/boot
TFTP网络启动(调试时最方便):
# 在开发机上
cp arch/arm/boot/uImage /tftpboot/
cp arch/arm/boot/dts/zynq-你的板子.dtb /tftpboot/
# 在uboot命令行
tftp 0x8000 uImage
tftp 0x2A00000 zynq-你的板子.dtb
bootm 0x8000 - 0x2A00000
我个人调试时喜欢用TFTP。改完内核,编译,tftp加载,重启,全程不到30秒。SD卡要插拔,太慢了。
3.3.3 验证内核是否正常
板子启动后,看串口输出。正常的话你会看到:
Starting kernel ...
Booting Linux on physical CPU 0x0
Linux version 5.15.0-xilinx-v2023.1 ...
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c5387d
...
Freeing unused kernel memory: 1024K
Run /sbin/init as init process
如果卡在某一步,比如"Waiting for root device",通常是设备树里根文件系统配置不对。检查chosen节点的bootargs:
chosen {
bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext4";
};
我曾经因为root=/dev/mmcblk0p2写成了mmcblk0p1,系统死活起不来。这种低级错误,查起来最费时间。
3.4 总结与建议
内核编译这件事,做多了就熟了。我给你三个建议:
- 版本锁定:确定一个内核版本后,不要轻易升级。Zynq的外设驱动跟内核版本绑定很紧,升级可能带来兼容性问题。
- 配置备份:每次调通一个配置,把
.config文件备份到Git里。我吃过亏,改了一堆配置后忘了保存,重来一遍想死的心都有。 - 从简到繁:第一次编译,用defconfig先跑通。确认能启动后,再慢慢加功能。别一上来就开一堆选项,出了问题你都不知道是哪步错了。
嗯,这一章就到这里。下一章我们讲根文件系统的构建,到时候你会看到内核和文件系统是怎么配合的。有问题随时翻翻这一章,编译命令和配置路径我都写得很清楚了。