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的黄金法则:

  1. 看内核日志:启动时加 earlycon 和 ignore_loglevel,把DTS解析过程打印出来。
  2. 用dt-utils工具:dtc -I dtb -O dts 反编译dtb,检查语法。
  3. 对比参考板:找一款官方支持的ARMv9开发板,比如FVP(Fixed Virtual Platform),对照它的DTS改。

我曾经调试一个PCIe控制器,死活枚举不到设备。折腾了两天,最后发现是DTS里 msi-parent 属性指向了错误的中断控制器。嗯,这种问题,光看代码很难发现,必须用 trace-cmd 抓中断路由的tracepoint才能定位。

3.4 知识体系:一张图看懂内核移植

下面这张SVG图,把整个内核移植的流程串起来了。你可以把它当作检查清单,每做完一步就打个勾。

ARMv9内核移植流程 ARMv9 内核移植核心流程 获取内核源码 内核配置与裁剪 DTS适配与调试 编译与生成镜像 启动与调试 失败回退,重新配置 关键检查点 1. Kconfig选项完整性 2. DTS语法正确性 3. 中断控制器配置 4. 内存地址范围 5. 时钟与复位 6. 调试串口输出 7. 文件系统挂载 8. 网络设备枚举

3.5 实战:从零开始配置一个ARMv9内核

假设你拿到一块基于 Cortex-A715 的板子。我会按以下步骤操作:

  1. 选择默认配置:make ARCH=arm64 defconfig。这会生成一个通用的ARMv8-A配置,ARMv9大部分兼容。
  2. 开启ARMv9特性:make menuconfig,进入 Kernel Features,勾选 ARMv8.5 architectural features 下的 FEAT_SVE 和 FEAT_RME。
  3. 裁剪驱动:进入 Device Drivers,关掉所有不用的硬件驱动。比如你的板子用 DesignWare 的USB控制器,那就只保留 Synopsys DesignWare 相关的选项。
  4. 配置DTS:在 arch/arm64/boot/dts/ 下创建板级DTS文件,包含 cortex-a715.dtsi,然后添加内存、串口、网卡等节点。
  5. 编译与测试:make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8。烧录到板子,看串口输出。

我的习惯: 第一次编译,我会加 V=1 参数,把编译命令全部打印出来。这样如果报错,能立刻知道是哪个文件出了问题。别嫌输出多,调试时这比猜谜强多了。

嗯,内核移植这件事,说难不难,说简单也不简单。关键是把每个环节的检查点都做到位。你按照上面的流程走一遍,遇到问题再回头看DTS和Kconfig,基本都能搞定。