3. 内核编译与烧录:获取源码、配置、编译与烧录

这一章,我们正式进入动手环节。说白了,就是要把 Linux 内核从源码变成能在 T113 上跑起来的二进制镜像,然后烧进板子。很多初学者觉得这步就是敲几条命令,其实不然。我在项目里见过太多人卡在“编译不过”或者“烧进去起不来”的坑里,最后发现是配置错了或者设备树没改对。

好,我们一步步来。

3.1 获取 Linux 内核源码

全志官方维护的 Linux 内核分支通常基于 linux-5.4 或 linux-5.10。我个人习惯用 5.4,因为社区资料多,踩过的坑基本都有解决方案。

获取源码有两种方式:

  1. 从全志官方 SDK 获取:通常是一个 tar.gz 压缩包,解压即可。
  2. 从 GitHub 拉取:全志有公开的仓库,但分支可能不完整。

我建议你用 SDK 里的源码,因为全志在发布 SDK 时已经验证过该内核版本与 T113 的硬件匹配度。我自己就吃过亏——从 GitHub 拉了个“最新”分支,结果网卡驱动死活不工作,折腾了两天才发现是 DMA 地址映射改了。

关键路径:解压后的源码目录结构大致如下:

linux-5.4/
├── arch/          # 架构相关代码(arm32/arm64)
├── drivers/       # 设备驱动
├── dts/           # 设备树源文件
├── include/       # 头文件
├── init/          # 内核初始化
├── kernel/        # 核心调度/进程管理
├── Makefile       # 顶层编译脚本
└── scripts/       # 编译辅助脚本

3.2 配置内核(menuconfig)

拿到源码后,第一件事不是直接编译,而是配置。配置决定了哪些驱动被编译进内核、哪些作为模块、哪些干脆不编译。

T113 的默认配置通常在 arch/arm/configs/ 或 arch/arm64/configs/ 下,文件名类似 sun8iw20p1_defconfig。加载它:

make ARCH=arm sun8iw20p1_defconfig

然后打开图形化配置界面:

make ARCH=arm menuconfig

你会看到一个蓝底黄字的菜单。这里我分享一个经验:不要试图一次性配完所有选项。你只需要关注几个关键区域:

配置路径 说明 我的建议
System Type → ARM system type 选择 ARM 架构类型 默认即可,全志已预置
Device Drivers → <*> DMA Engine support DMA 引擎,外设高速传输必备 建议编入内核,不要模块
Device Drivers → Network device support 网卡驱动(如 GMAC) 根据你的板载 PHY 芯片选择
File systems → <*> ext4 根文件系统类型 如果 rootfs 是 ext4,必须编入
Boot options → Kernel command line 内核启动参数 console=ttyS0,115200 等

小技巧:在 menuconfig 里按 / 可以搜索符号。比如你想找“GPIO”相关的配置,按 / 输入 GPIO,它会列出所有包含 GPIO 的选项和路径。这比手动翻菜单快得多。

配置完成后,保存退出。配置会写入 .config 文件。我建议你备份一份:

cp .config config-my-t113-board

为什么?因为如果你后面改了配置导致编译失败,可以快速回退。我曾经在一次调试中连续改了 20 多次配置,每次都要重新选,后来学乖了——每次改之前先备份。

3.3 编译内核与设备树

配置搞定,接下来就是编译。T113 是双核 Cortex-A7,我一般用 -j4 并行编译:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 dtbs

这里解释一下:

  • zImage:压缩后的内核镜像,U-Boot 能直接加载。
  • dtbs:设备树二进制文件,描述硬件拓扑。

编译完成后,在 arch/arm/boot/ 下会生成 zImage,在 arch/arm/boot/dts/ 下生成 *.dtb 文件。

注意:如果你的 T113 是 64 位模式(ARMv8),ARCH 要改成 arm64,交叉编译器也要换成 aarch64-linux-gnu-。32 位和 64 位的设备树文件不通用,千万别搞混。

编译过程中如果报错,90% 的情况是以下原因:

  1. 交叉编译器版本不对:全志推荐 gcc 7.x 或 8.x,太新的版本可能不兼容。
  2. 缺少依赖库:比如 ncurses-dev 没装,menuconfig 打不开。
  3. .config 里有未解析的符号:通常是某个驱动依赖了另一个没选的驱动。

遇到错误别慌,先看最后几行报错信息。我一般用 2>&1 | tail -50 把错误重定向出来,定位很快。

3.4 烧录镜像到 T113 开发板

编译出 zImage 和 .dtb 后,下一步就是烧录。T113 的启动方式通常是从 SD 卡 或 NAND Flash 启动。我以 SD 卡为例,因为调试最方便。

烧录流程分三步:

  1. 准备 SD 卡:分区、格式化。
  2. 复制镜像:把 zImage、dtb、U-Boot、rootfs 拷进去。
  3. 插卡上电:看串口输出。

具体命令如下(假设 SD 卡设备是 /dev/sdb):

# 1. 分区(用 fdisk 或 parted)
sudo fdisk /dev/sdb
# 创建两个分区:第一个 64MB 放 boot,第二个放 rootfs

# 2. 格式化
sudo mkfs.vfat /dev/sdb1   # boot 分区用 FAT32
sudo mkfs.ext4 /dev/sdb2   # rootfs 分区用 ext4

# 3. 挂载并复制
sudo mount /dev/sdb1 /mnt/boot
sudo cp arch/arm/boot/zImage /mnt/boot/
sudo cp arch/arm/boot/dts/sun8i-t113.dtb /mnt/boot/
sudo umount /mnt/boot

# 4. 烧录 U-Boot(如果 SD 卡没有)
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8

关键点:U-Boot 的烧录位置是固定的。全志 T113 的 SPL(Secondary Program Loader)从 SD 卡的 第 8KB 偏移 开始读取。如果你烧错位置,板子根本不会启动。

烧完后,插卡上电,串口连接(波特率 115200),你会看到类似这样的输出:

U-Boot SPL 2020.01 (Jan 10 2024 - 15:30:00 +0800)
DRAM: 512 MiB
Trying to boot from MMC1

U-Boot 2020.01 (Jan 10 2024 - 15:30:00 +0800)
CPU: Allwinner T113 (SUN8I)
...
Loading kernel from MMC: zImage ...
Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.4.61
...

看到 Booting Linux on physical CPU 0x0,说明内核已经跑起来了。如果卡在某一步不动,比如停在 Starting kernel ... 之后没反应,通常是设备树和内核不匹配,或者根文件系统没挂上。

3.5 知识结构图

下面这张图总结了本章的核心流程:

内核编译与烧录流程 内核编译与烧录核心流程 获取内核源码 SDK / GitHub 内核配置 menuconfig / defconfig 编译内核 make zImage + dtbs 烧录镜像 SD卡 / NAND Flash 关键产出物 • zImage(内核镜像) • *.dtb(设备树) • .config(配置备份) • u-boot-sunxi-with-spl.bin 常见问题 • 编译器版本不匹配 • 设备树与内核不一致 • U-Boot 烧录偏移错误 流程总结:获取源码 → 配置内核 → 编译镜像 → 烧录启动

3.6 避坑指南

最后,我把自己踩过的几个坑列出来,你遇到了可以直接对号入座:

  • 烧录后串口无输出:检查 U-Boot 是否烧对位置。我曾经把 seek=8 写成了 seek=8K,结果板子一直黑屏。
  • 内核启动到一半卡死:多半是设备树里的 reg 地址和实际硬件不匹配。用 cat /proc/iomem 对比一下。
  • 根文件系统挂载失败:检查内核是否编译了对应的文件系统驱动。比如你用 ext4,但内核里没编入 ext4 支持,那肯定挂不上。
  • 网络不通:PHY 芯片的复位时序可能不对。在设备树里加个 reset-gpios 属性试试。

嗯,这一章的内容就到这里。配置和编译看似枯燥,但它是后续所有驱动开发的基础。你只要把这一步跑通,后面写驱动、调外设都会顺畅很多。