1. 设备树技术背景与龙芯平台特性
设备树(Device Tree)作为现代嵌入式系统中的硬件描述标准,已经彻底改变了传统内核硬编码的架构模式。在龙芯2K1000/2K1500这类国产MIPS架构处理器上,设备树的引入尤为重要——它解决了传统嵌入式开发中"每款板卡一个内核"的维护困境。
我最早接触设备树是在2016年为龙芯2K1000开发定制载板时。当时发现同一个内核镜像通过不同的dtb文件就能适配不同硬件配置,这种"硬件抽象"的设计让我印象深刻。设备树本质上是一种硬件拓扑结构的树形数据结构,包含:
- 节点(Node):对应物理或逻辑设备
- 属性(Property):描述设备的寄存器地址、中断号等参数
- 绑定(Binding):定义节点必须/可选的属性规范
以龙芯2K1500的串口控制器为例,其设备树节点会包含:
dts复制uart0: serial@1fe20000 {
compatible = "loongson,ls2k-uart";
reg = <0x1fe20000 0x100>;
interrupts = <12>;
clocks = <&uart_clk>;
};
这种声明式写法比传统C代码中的platform_device_register()直观得多。龙芯平台的特殊性在于:
- 采用MIPS64架构但设备树标准与ARM保持兼容
- 部分IP核(如DMA控制器)使用自主设计,需要定制绑定
- 启动流程中由PMON(类似U-Boot)负责初始设备树传递
2. 设备树编译与二进制格式解析
设备树从源文件到内核可用的过程要经历两个关键阶段:
2.1 DTS到DTB的编译过程
使用dtc编译器时的典型命令:
bash复制dtc -I dts -O dtb -o 2k1000.dtb 2k1000.dts
这个过程中会发生的重要转换包括:
- 宏展开:处理
#include和#define等预处理指令 - 语法检查:验证节点命名、属性值类型等
- 结构扁平化:将层级结构转换为线性内存布局
- 字符串池优化:合并重复字符串减少体积
龙芯平台常见的编译陷阱:
- 地址转换问题:龙芯使用64位物理地址,需确认
reg属性是否带0x前缀 - 端序标记:DTB文件头中的
bswap标志需与CPU端序匹配 - 保留内存区域:龙芯的
reserved-memory节点需要特殊处理
2.2 DTB二进制格式深度剖析
通过fdtdump工具查看DTB结构:
code复制// 示例输出片段
/dts-v1/;
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
/memreserve/ 0x0000000000000000 0x0000000000001000;
/ {
#address-cells = <0x00000002>;
#size-cells = <0x00000002>;
compatible = "loongson,ls2k1000";
...
};
关键数据结构解析:
- Header:魔数、版本、时间戳等元信息
- Memory Reserve Map:内核不可使用的内存区域
- Structure Block:设备树主体,采用深度优先遍历序列化
- Strings Block:所有属性名的字符串池
- Symbols Block(可选):供动态加载的符号表
在龙芯平台上验证DTB完整性的技巧:
bash复制# 检查文件头
hexdump -C 2k1000.dtb | head -n 5
# 验证结构完整性
dtc -I dtb -O dts 2k1000.dtb > /dev/null
3. 内核设备树解析全流程
3.1 早期初始化阶段
龙芯平台的设备树加载始于arch/mips/kernel/head.S中的汇编代码。关键步骤:
- 物理地址转换:PMON将DTB物理地址存入$a0寄存器
- 初步校验:检查魔数(0xd00dfeed)和版本
- 保留内存设置:处理
memreserve条目 - 重定位:将DTB复制到内核安全区域
这个阶段需要特别注意:
- 龙芯2K1000的缓存一致性配置会影响DTB访问
- 某些开发板可能修改PMON导致DTB传递异常
- 早期printk不可用,需要通过LED或串口原始输出调试
3.2 设备树展开流程
在setup_arch()中完成的核心操作:
c复制// arch/mips/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
early_init_devtree(initial_boot_params);
...
unflatten_device_tree();
}
unflatten_device_tree()的关键动作:
- 分配
device_node结构体 - 解析每个节点生成内核链表
- 处理特殊属性(
phandle,status等) - 建立
of_root全局根节点
龙芯平台的特殊处理:
- 需要修正
dma-coherent属性的解析 - 自主IP核节点需提前注册匹配表
- 多核启动时需处理
cpu-map节点
3.3 设备树到platform_device的转换
内核通过of_platform_default_populate()自动创建平台设备:
c复制// drivers/of/platform.c
int of_platform_default_populate(...)
{
for_each_child_of_node(root, child) {
if (!of_node_check_flag(child, OF_POPULATED))
of_platform_device_create(child, NULL, NULL);
}
}
转换规则:
- 具有
compatible属性的节点生成platform_device reg和interrupts属性转换为资源- 其他属性存入
dev.of_node->properties
龙芯2K1500的典型问题:
- 自主GPU节点需要手动注册而非自动转换
- PCIe主机控制器需要特殊资源处理
- 某些传感器节点需延迟加载
4. 龙芯平台设备树调试实战
4.1 运行时设备树检查
通过sysfs接口实时查看设备树:
bash复制# 查看节点属性
ls /proc/device-tree/soc/
# 读取寄存器地址
hexdump -C /proc/device-tree/soc/uart@1fe20000/reg
龙芯专用调试工具:
bash复制# 内核配置开启CONFIG_LOONGSON_DTB_DEBUG
echo 1 > /sys/kernel/debug/loongson_dtb/debug_level
dmesg | grep dtb
4.2 常见问题排查指南
问题1:内核启动时卡在"Unflattening device tree..."
- 检查DTB文件是否完整:
dtc -I dtb -O dts -f 2k1000.dtb - 确认PMON传递的地址正确:在内核early_printk中输出$a0值
- 排查内存冲突:检查
memreserve区域是否与内核重叠
问题2:设备驱动probe失败但节点存在
bash复制# 确认节点状态
cat /proc/device-tree/soc/i2c@1fe30000/status
# 检查时钟资源
ls /sys/kernel/debug/clk/ | grep i2c
# 验证中断映射
cat /proc/interrupts | grep 1fe30000
问题3:自定义外设无法识别
- 确认绑定文档是否正确:
dts复制my_device {
compatible = "loongson,my-device";
reg = <0x1ff00000 0x1000>;
interrupts = <0 15 4>;
loongson,custom-prop = <0x1234>;
};
- 检查驱动匹配表:
c复制static const struct of_device_id my_dev_ids[] = {
{ .compatible = "loongson,my-device" },
{}
};
4.3 性能优化技巧
- DTB裁剪:
bash复制# 移除调试信息
dtc -R 8 -p 0x1000 -O dtb -o lean.dtb full.dts
- 预解析缓存:
在内核配置中启用CONFIG_OF_OVERLAY和CONFIG_OF_FLATTREE_CACHE - 延迟加载:
对非关键设备添加status = "disabled",运行时通过echo 1 > /sys/devices/.../of_node/status激活
5. 龙芯设备树开发进阶
5.1 自定义绑定开发
为龙芯特有硬件编写绑定文档示例:
yaml复制# Documentation/devicetree/bindings/misc/loongson,my-device.yaml
description: Loongson Custom IP Block
properties:
compatible:
const: loongson,my-device
reg:
maxItems: 1
interrupts:
maxItems: 1
loongson,clock-frequency:
$ref: /schemas/types.yaml#/definitions/uint32
required:
- compatible
- reg
验证绑定:
bash复制make dt_binding_check DT_SCHEMA_FILES=loongson,my-device.yaml
5.2 设备树覆盖技术
动态加载设备树片段:
bash复制# 准备覆盖层
fdtoverlay -i base.dtb -o merged.dtb overlay.dtbo
# 内核运行时加载
echo overlay.dtbo > /sys/kernel/config/device-tree/overlays/load
龙芯平台注意事项:
- 需要确保PMON预留足够的DTB空间
- 覆盖层不能修改保留内存区域
- 复杂覆盖建议使用
dtc -@生成符号表
5.3 安全加固方案
- 设备树签名验证:
bash复制# 生成密钥
openssl genrsa -out key.pem 2048
# 签名
dtc -I dts -O dtb -S key.pem -s 2k1000.dts -o signed.dtb
- 内核配置开启
CONFIG_OF_OVERLAY_FIRMWARE_CMDLINE - 关键节点保护:
dts复制/ {
secure {
#address-cells = <1>;
#size-cells = <1>;
ranges;
crypto@1ff80000 {
compatible = "loongson,ls2k-crypto";
reg = <0x1ff80000 0x10000>;
no-map;
};
};
};