3. 内核移植实战:Linux内核配置与裁剪、ARMv9特定Kconfig选项、DTS适配与调试
内核移植,说白了就是让Linux内核认识你的硬件。ARMv9的移植,跟之前ARMv8比,多了不少新东西。我刚开始做ARMv9移植时,也踩过几个坑,今天把这些经验拆开揉碎了讲给你听。
3.1 内核配置与裁剪:别让内核“虚胖”
一个完整的内核源码树,编译出来动辄几十兆。对于嵌入式系统来说,这太奢侈了。裁剪的核心思路就一句话:用不到的,统统去掉。
我个人习惯,拿到一块新板子,第一步不是急着改代码,而是先跑一遍 make defconfig,生成一个默认配置。然后基于这个配置,逐项裁剪。
裁剪三原则:
- 架构无关的驱动:比如你用的是eMMC,那NAND Flash的驱动就可以关掉。
- 调试与测试模块:像
CONFIG_DEBUG_FS、CONFIG_KPROBES,产品阶段必须关。 - 不用的文件系统:只用ext4,就别编译btrfs、xfs。
具体操作上,我推荐用 make menuconfig。它基于ncurses,界面虽然复古,但搜索功能(按 / 键)非常实用。比如你想找ARMv9相关的选项,直接搜 ARM64_VA_BITS,就能定位到虚拟地址位宽配置。
小技巧: 用 make savedefconfig 生成精简的配置文件。它会把所有非默认值的选项列出来,方便你版本管理。我每次裁剪完,都会跑一遍这个命令,把结果提交到Git里。
3.2 ARMv9特定Kconfig选项:新架构的新玩法
ARMv9相比v8,最大的变化是引入了 RME(Realm Management Extension) 和 FEAT_SVE2。这些特性在内核里都有对应的Kconfig开关。
我整理了一个表格,把最关键的几个选项列出来:
| Kconfig 选项 | 功能描述 | 我的建议 |
|---|---|---|
CONFIG_ARM64_VA_BITS_52 |
启用52位虚拟地址 | 如果内存超过4TB,必须开 |
CONFIG_ARM64_PA_BITS_52 |
启用52位物理地址 | 配合硬件支持,一般不开 |
CONFIG_ARM64_SVE |
启用SVE(可伸缩向量扩展) | ARMv9必开,性能提升明显 |
CONFIG_ARM64_SME |
启用SME(流式SVE扩展) | 看具体IP,部分核支持 |
CONFIG_REALM |
启用RME支持 | 安全场景必开,否则关掉 |
为什么会特别强调 CONFIG_ARM64_SVE?我在项目中遇到过,某款ARMv9芯片的AI加速器依赖SVE指令集。如果内核没开这个选项,驱动加载时直接报 undefined instruction。嗯,这里要注意,SVE的使能不光要开Kconfig,还要确保bootloader里把 ID_AA64PFR0_EL1.SVE 字段配置正确。
避坑指南: 我曾经在移植时,开了 CONFIG_ARM64_SVE 但没开 CONFIG_ARM64_SVE2,结果SVE2相关的优化指令全部失效。这两个选项是独立的,别想当然以为SVE包含了SVE2。
3.3 DTS适配与调试:让内核认识你的板子
DTS(Device Tree Source)是内核与硬件之间的“翻译官”。ARMv9的DTS编写,跟之前比,多了几个必须关注的节点。
先看一个典型的内存节点:
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>;
};
这段代码告诉内核,物理内存从 0x80000000 开始,大小是 0x80000000(2GB)。你想想看,如果这里写错了,内核启动到一半就会panic。
ARMv9特有的 GIC-600 中断控制器,DTS写法也有讲究:
gic: interrupt-controller@2f000000 {
compatible = "arm,gic-600";
reg = <0x0 0x2f000000 0 0x10000>,
<0x0 0x2f100000 0 0x200000>;
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
};
注意 reg 属性里的两个地址范围。第一个是GIC distributor,第二个是redistributor。ARMv9的GIC-600要求redistributor区域必须连续,否则中断无法正常路由。
调试DTS的黄金法则:
- 看内核日志:启动时加
earlycon和ignore_loglevel,把DTS解析过程打印出来。 - 用dt-utils工具:
dtc -I dtb -O dts反编译dtb,检查语法。 - 对比参考板:找一款官方支持的ARMv9开发板,比如FVP(Fixed Virtual Platform),对照它的DTS改。
我曾经调试一个PCIe控制器,死活枚举不到设备。折腾了两天,最后发现是DTS里 msi-parent 属性指向了错误的中断控制器。嗯,这种问题,光看代码很难发现,必须用 trace-cmd 抓中断路由的tracepoint才能定位。
3.4 知识体系:一张图看懂内核移植
下面这张SVG图,把整个内核移植的流程串起来了。你可以把它当作检查清单,每做完一步就打个勾。
3.5 实战:从零开始配置一个ARMv9内核
假设你拿到一块基于 Cortex-A715 的板子。我会按以下步骤操作:
- 选择默认配置:
make ARCH=arm64 defconfig。这会生成一个通用的ARMv8-A配置,ARMv9大部分兼容。 - 开启ARMv9特性:
make menuconfig,进入Kernel Features,勾选ARMv8.5 architectural features下的FEAT_SVE和FEAT_RME。 - 裁剪驱动:进入
Device Drivers,关掉所有不用的硬件驱动。比如你的板子用DesignWare的USB控制器,那就只保留Synopsys DesignWare相关的选项。 - 配置DTS:在
arch/arm64/boot/dts/下创建板级DTS文件,包含cortex-a715.dtsi,然后添加内存、串口、网卡等节点。 - 编译与测试:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8。烧录到板子,看串口输出。
我的习惯: 第一次编译,我会加 V=1 参数,把编译命令全部打印出来。这样如果报错,能立刻知道是哪个文件出了问题。别嫌输出多,调试时这比猜谜强多了。
嗯,内核移植这件事,说难不难,说简单也不简单。关键是把每个环节的检查点都做到位。你按照上面的流程走一遍,遇到问题再回头看DTS和Kconfig,基本都能搞定。