2、ARM64设备树基础:设备树(DTB/DS)结构、节点与属性、地址编码与中断映射
2.1 设备树概述
设备树(Device Tree)是一种描述硬件配置的数据结构,用于将硬件信息从固件传递给操作系统。在ARM64平台中,设备树取代了传统x86平台的ACPI,成为主流的硬件描述机制。设备树的核心文件包括:
- .dts(Device Tree Source):人类可读的文本源文件,使用类似C语言的语法描述硬件。
- .dtb(Device Tree Blob):由DTS编译生成的二进制文件,是UEFI固件与操作系统内核之间传递硬件信息的标准格式。
- .dtsi(Device Tree Source Include):可被多个DTS文件引用的公共头文件,用于复用SoC级硬件定义。
2.2 设备树结构
设备树采用树形层次结构,从单一的根节点(/)开始,向下扩展出多个子节点,每个节点代表一个硬件设备或总线。下图展示了典型的ARM64设备树结构:
2.3 节点与属性
2.3.1 节点定义
每个设备树节点由节点名称和一对花括号组成,节点内可以包含属性(Property)和子节点。节点名称的格式为 name@unit-address,其中 unit-address 通常对应设备的寄存器基地址或单元编号。
/dts-v1/;
/ {
model = "ARM64 Reference Platform";
compatible = "arm,arm64-ref-platform";
#address-cells = <2>;
#size-cells = <2>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a76";
reg = <0x0>;
enable-method = "psci";
};
cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a76";
reg = <0x1>;
enable-method = "psci";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>;
};
};
2.3.2 核心属性
| 属性名 | 类型 | 说明 | 示例 |
|---|---|---|---|
compatible |
字符串列表 | 设备兼容性标识,内核据此匹配驱动 | "arm,cortex-a76" |
reg |
整数数组 | 设备寄存器地址与长度,格式由 #address-cells 和 #size-cells 决定 |
<0x0 0x80000000 0x0 0x80000000> |
device_type |
字符串 | 设备类型,主要用于 CPU 和内存节点 | "cpu"、"memory" |
interrupt-parent |
phandle | 指定该设备的中断控制器父节点 | <&gic> |
interrupts |
整数数组 | 描述设备的中断号、触发类型等信息 | <0 42 4> |
clocks |
phandle列表 | 设备使用的时钟源引用 | <&uart_clk> |
status |
字符串 | 设备状态:"okay" 启用,"disabled" 禁用 |
"okay" |
compatible 属性与内核驱动不匹配,会导致设备无法被识别。建议使用 dtc -I dtb -O dts 反编译DTB,检查属性值是否正确。
2.4 地址编码
ARM64设备树使用 #address-cells 和 #size-cells 属性定义地址和长度的编码宽度。这两个属性在父节点中定义,影响所有子节点的 reg 属性解析。
2.4.1 地址编码规则
- #address-cells:表示地址字段占用的32位单元数。ARM64平台通常设为
<2>,支持64位地址空间。 - #size-cells:表示长度字段占用的32位单元数。ARM64平台通常设为
<2>,支持64位长度描述。 - reg:格式为
<address1 length1 address2 length2 ...>,每个地址/长度对描述一段连续的寄存器或内存区域。
// 64位地址空间示例
soc {
#address-cells = <2>; // 地址用2个32位单元表示(64位)
#size-cells = <2>; // 长度用2个32位单元表示(64位)
uart@1c090000 {
compatible = "arm,pl011";
reg = <0x0 0x1c090000 0x0 0x1000>;
// 地址: 0x1c090000, 长度: 0x1000
interrupts = <0 39 4>;
};
dma@1c0a0000 {
compatible = "arm,pl330";
reg = <0x0 0x1c0a0000 0x0 0x1000>;
// 地址: 0x1c0a0000, 长度: 0x1000
};
};
2.4.2 地址映射与ranges属性
ranges 属性用于定义父节点地址空间与子节点地址空间之间的映射关系。当子节点地址需要转换为父节点地址空间时,ranges 提供转换表。
| ranges格式 | 说明 |
|---|---|
ranges;(空) |
子节点地址与父节点地址空间一一对应,无需转换 |
ranges = <child-bus-address parent-bus-address length>; |
将子节点地址 child-bus-address 映射到父节点地址 parent-bus-address,映射长度为 length |
不定义 ranges |
子节点地址空间与父节点隔离,设备驱动需通过特定方法访问 |
// ranges 映射示例
pcie@40000000 {
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x01000000 0x0 0x00000000 0x0 0x3eff0000 0x0 0x00010000
0x02000000 0x0 0x40000000 0x0 0x40000000 0x0 0x40000000>;
// 第1行: I/O空间映射
// 第2行: 32位非预取内存映射
};
2.5 中断映射
ARM64设备树的中断映射涉及中断控制器、中断描述符和中断父节点三个核心概念。中断映射的准确性直接影响设备中断功能的正常使用。
2.5.1 中断控制器节点
中断控制器节点使用 interrupt-controller 属性标识自身为中断控制器,#interrupt-cells 属性定义中断描述符的字段数量。
// GICv3 中断控制器节点示例
gic: interrupt-controller@2f000000 {
compatible = "arm,gic-v3";
reg = <0x0 0x2f000000 0x0 0x10000>,
<0x0 0x2f100000 0x0 0x200000>;
interrupt-controller;
#interrupt-cells = <3>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
};
2.5.2 中断描述符格式
ARM64平台常用的中断描述符格式由 #interrupt-cells 决定。以GICv3为例,#interrupt-cells = <3>,三个字段的含义如下:
| 字段 | 位置 | 说明 | 取值 |
|---|---|---|---|
| 中断类型 | 第1个单元 | 0=SPI共享外设中断,1=PPI私有外设中断 | 0 或 1 |
| 中断号 | 第2个单元 | SPI中断号范围32~1019,PPI中断号范围16~31 | 42、29 |
| 触发类型 | 第3个单元 | 1=上升沿触发,4=高电平触发,8=下降沿触发 | 1、4、8 |
// 设备中断映射示例
uart@1c090000 {
compatible = "arm,pl011";
reg = <0x0 0x1c090000 0x0 0x1000>;
interrupt-parent = <&gic>; // 引用GIC中断控制器
interrupts = <0 39 4>; // SPI中断39,高电平触发
};
eth@1c100000 {
compatible = "realtek,rtl8169";
reg = <0x0 0x1c100000 0x0 0x10000>;
interrupt-parent = <&gic>;
interrupts = <0 45 1>; // SPI中断45,上升沿触发
};
2.5.3 中断映射流程
下图展示了设备树中断映射的完整流程,从设备节点到中断控制器再到CPU:
2.6 设备树编译与调试
2.6.1 常用工具
| 工具 | 功能 | 常用命令 |
|---|---|---|
dtc |
设备树编译器/反编译器 | dtc -I dtb -O dts -o output.dts input.dtb |
fdtdump |
以可读格式转储DTB内容 | fdtdump input.dtb |
fdtget |
从DTB中读取指定节点/属性 | fdtget input.dtb /cpus/cpu@0 compatible |
fdtput |
向DTB中写入指定节点/属性 | fdtput input.dtb /chosen bootargs "console=ttyAMA0" |
2.6.2 常见调试问题
- DTB地址对齐错误:UEFI固件放置DTB时未按8字节对齐,导致内核解析失败。检查
device-tree-base启动参数。 - 中断映射不匹配:设备节点的
interrupts字段数量与中断控制器的#interrupt-cells不一致。使用dtc反编译验证。 - 地址编码错误:
#address-cells和#size-cells设置错误导致reg解析异常。ARM64平台应统一使用<2>。 - compatible属性缺失:设备节点缺少
compatible属性,内核无法匹配驱动。检查设备树源文件。
dmem 命令查看DTB所在内存区域的内容,验证DTB魔数(0xD00DFEED)是否正确。内核启动参数中增加 earlycon 和 debug 可以输出设备树解析阶段的调试信息。
2.7 本章小结
设备树是ARM64平台硬件描述的核心机制,理解其结构、节点属性、地址编码和中断映射是进行EFI Stub引导调试的基础。关键要点包括:
- 设备树采用树形层次结构,根节点下包含CPU、内存、SoC外设等子节点。
- 节点通过
compatible、reg、interrupts等属性描述硬件特征。 - 地址编码由
#address-cells和#size-cells控制,ARM64平台通常使用64位编码。 - 中断映射通过
interrupt-parent和interrupts属性建立设备到中断控制器的连接。 - 使用
dtc、fdtdump等工具可以高效调试设备树问题。