4. 内核编译基础:配置内核 .config 文件、编译内核与模块、编译设备树

在成功获取内核源码并安装交叉编译工具链后,下一步是配置内核、编译内核镜像与模块,以及生成设备树二进制文件(.dtb)。这是 DRM/KMS 驱动开发的基础操作,直接影响驱动能否正确加载与运行。

4.1 内核配置基础与 .config 文件

Linux 内核的编译行为由顶层 .config 文件控制。该文件记录了所有内核选项的开关状态(y 编译进内核、m 编译为模块、n 不编译)。对于 DRM/KMS 驱动开发,必须确保以下选项被正确启用:

配置选项 推荐值 说明
CONFIG_DRM y 启用 DRM 核心框架
CONFIG_DRM_KMS_HELPER y 启用 KMS 辅助层
CONFIG_DRM_XXX(具体驱动) y 或 m 目标 SoC 的 DRM 驱动(如 CONFIG_DRM_IMX)
CONFIG_FB n 建议关闭传统 fbdev,避免与 DRM 冲突
CONFIG_OF y 设备树支持,ARM/ARM64 平台必须
配置方法:推荐使用 make menuconfig 进行交互式配置。该命令基于 ncurses 提供图形化菜单,支持按 / 搜索选项,适合初学者和调试场景。

4.1.1 配置流程

  1. 加载默认配置:使用厂商提供的 defconfig,例如 make ARCH=arm imx_v7_defconfig。
  2. 定制配置:运行 make menuconfig ARCH=arm,进入 Device Drivers → Graphics support 路径,确认 DRM 相关选项。
  3. 保存配置:退出 menuconfig 时选择 Save,配置写入 .config 文件。
  4. 备份配置:将 .config 复制为 my_drm_defconfig,便于后续复用。
常见错误:直接修改 .config 文件而不运行 make olddefconfig 同步依赖,会导致编译时出现未定义的宏或选项冲突。修改后务必执行 make olddefconfig ARCH=arm。

4.2 编译内核与模块

内核编译分为两个主要产物:内核镜像(zImage/Image)和可加载模块(.ko)。DRM 驱动通常以模块形式编译,便于开发阶段快速迭代。

4.2.1 编译命令详解

# 编译内核镜像与所有模块
make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules

# 仅编译指定模块(加速调试)
make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- drivers/gpu/drm/imx/

# 安装模块到临时目录
make modules_install ARCH=arm INSTALL_MOD_PATH=./modules_install
参数 作用
-j$(nproc) 并行编译,使用所有 CPU 核心
ARCH=arm 指定目标架构
CROSS_COMPILE=... 交叉编译工具链前缀
zImage 编译压缩内核镜像(ARM 常用)
modules 编译所有标记为 m 的模块

4.2.2 编译流程示意图

内核编译流程 .config 配置 make zImage 内核镜像 make modules .ko 模块文件 modules_install 提示: 先编译内核镜像,再编译模块 模块依赖内核头文件

4.3 编译设备树(Device Tree Blob)

设备树是 ARM/ARM64 平台描述硬件拓扑的标准方式。DRM/KMS 驱动通过设备树节点获取显示控制器、HDMI/DP 编码器、面板等外设的地址、中断、时钟和 GPIO 信息。编译设备树的命令为 make dtbs。

4.3.1 设备树编译命令

# 编译所有设备树
make dtbs ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

# 编译单个设备树(加速调试)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
  arch/arm/boot/dts/imx6q-sabresd.dtb

4.3.2 设备树与 DRM 驱动的关系

一个典型的 DRM 设备树节点包含以下关键属性:

/* imx6qdl.dtsi 中的 IPU 显示节点示例 */
&ipu1 {
  compatible = "fsl,imx6q-ipu";
  reg = <0x02400000 0x400000>;
  interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>,
               <0 5 IRQ_TYPE_LEVEL_HIGH>;
  clocks = <&clks IMX6QDL_CLK_IPU1>;
  clock-names = "bus";

  port@0 {
    reg = <0>;
    ipu1_di0: endpoint {
      remote-endpoint = <&hdmi_mux_0>;
    };
  };
};
属性 作用 DRM 驱动使用场景
compatible 匹配驱动 of_match_table 查找
reg 寄存器地址范围 devm_ioremap_resource
interrupts 中断号 devm_request_irq
clocks 时钟源 clk_get / clk_prepare_enable
remote-endpoint 显示管道连接 drm_of_find_possible_crtcs

4.3.3 设备树编译流程

设备树编译流程 .dts 源文件 DTC 编译器 .dtb 二进制 .dtsi 包含文件 注意: 修改 .dts 后必须重新编译 .dtb U-Boot 加载 .dtb 传递给内核

4.4 编译产物验证

编译完成后,建议检查以下关键文件是否存在:

# 内核镜像
ls -lh arch/arm/boot/zImage

# 设备树
ls -lh arch/arm/boot/dts/imx6q-sabresd.dtb

# 模块(以 DRM 为例)
find drivers/gpu/drm/ -name "*.ko" | head -5
开发建议:在 DRM 驱动开发阶段,建议将驱动编译为模块(m),配合 insmod/rmmod 快速迭代。每次修改驱动源码后,只需重新编译对应模块并复制到目标板,无需重新烧写内核镜像。

4.5 常见编译错误与解决

错误现象 原因 解决方法
*** No rule to make target 'zImage' ARCH 未设置或错误 检查 ARCH=arm 是否正确
undefined reference to `drm_atomic_helper_xxx' CONFIG_DRM_KMS_HELPER 未开启 启用 CONFIG_DRM_KMS_HELPER=y
Error: unrecognized/unsupported machine ID 设备树未正确编译或未加载 确认 make dtbs 成功,检查 U-Boot 加载地址
multiple definition of `xxx' 同一符号被多次编译 检查 Makefile 是否重复添加源文件