嵌入式系统内核镜像相关(四)

文章目录

前言
一、介绍

1.1 目的
1.2 和IEEE 1275与ePAR的关系
1.3 32-bit和64-bit的支持
1.4 术语定义

二、设备树

2.1 概述
2.2 设备树结构

2.2.1 节点名称
2.2.2 通用名称推荐
2.2.3 路径名称
2.2.4 属性

2.2.4.1 属性名
2.2.4.2 属性值

2.3 标准属性

2.3.1 compatible
2.3.2 model
2.3.3 phandle
2.3.4 statuts
2.3.5 #address-cells和#size-cells
2.3.6 reg
2.3.7 virtual-reg
2.3.8 ranges
2.3.9 dma-ranges
2.3.10 dma-coherent
2.3.11 dma-noncoherent
2.3.12 name(已废弃)
2.3.13 device_type(已废弃)

2.4 中断和中断映射

2.4.1 Interrupt Generating Devices的属性

2.4.1.1 interrupt
2.4.1.2 interrupt-parent
2.4.1.3 interrupts-extended

2.4.2 Interrupt Controllers的属性

2.4.2.1 #interrupt-cells
2.4.2.2 interrupt-controller

2.4.3 Interrupt中继(Nexus)属性

2.4.3.1 interrupt-map
2.4.3.2 interrupt-map-mask
2.4.3.3 interrupt-cells

2.4.4 Interrupt映射的例子

2.5 中继(Nexus)节点与说明符(specifier)映射

2.5.1 中继(Nexus)节点属性

2.5.1.1 `<`specifier`>`-map
2.5.1.2 `<`specifier`>`-map-mask
2.5.1.3 `<`specifier`>`-map-pass-thru
2.5.1.4 #`<`specifier`>`-cells

2.5.2 说明符映射的例子

三、设备节点规格要求

3.1 基本设备节点类型
3.2 根节点
3.3 /aliases节点
3.4 /memory节点

3.4.1 /memory节点和UEFI
3.4.2 /memory例子

3.5 /reserved-memory节点

3.5.1 /reserved-memory parent节点
3.5.2 /reserved-memory/ child节点
3.5.3 对reserved memory的设备节点引用
3.5.4 /reserved memory和UEFI
3.5.5 /reserved memory例子

3.6 /chosen节点
3.7 /cpus节点属性
3.8 /cpus/cpu*节点属性

3.8.1 /cpus/cpu*节点的通用属性
3.8.2 TLB属性
3.8.3 Internal(L1)Cache属性
3.8.4 例子

3.9 多层次和共享Cache节点(/cpus/cpu*/l?-cache)

3.9.1 例子

四、设备绑定(Device Bindings)

4.1 绑定指南

4.1.1 通用原则
4.1.2 杂项属性

4.2 串行设备

4.2.1 串行类绑定
4.2.2 National Semiconductor 16450/16550 兼容UART要求

4.3 网络设备

4.3.1 网络类绑定
4.3.2 以太网特定考量

4.4 Power ISA 开放可编程中断控制器(PIC)
4.5 simple-bus(简单总线)兼容值

五、扁平化设备树格式(DTB)

5.1 版本管理
5.2 头部
5.3 Memory Reservation Block

5.3.1 目的
5.3.2 格式
5.3.3 Memory Reservation Block和UEFI

5.4 Structure Block

5.4.1 词法结构
5.4.2 树结构

5.5 Strings Block
5.6 对齐

六、设备树源格式(DTS)

6.1 编译器指令
6.2 标签
6.3 节点和属性定义
6.4 文件布局

总结


前言

这一篇主要翻译设备树语法,参考devicetree官网的《Devicetree Specification
Release v0.4》。

直接机翻了!若读者发现翻译错误,请不吝指出,感激不尽!

因为只是私人翻译学习使用,所以还请不要用于商业用途。

贴上原文的许可证要求:


一、介绍

1.1 目的

为了初始化和启动计算机系统,各种软件组件相互作用。固件(Firmware)可能会执行系统硬件的低级初始化,然后将控制权交给软件,如操作系统(Operating System)、引导加载程序(Bootloader)或虚拟机监视器(Hypervisor)。

引导加载程序和虚拟机监视器反过来可以加载并转移控制权给操作系统。标准、一致的接口和约定有助于这些软件组件之间的交互。在本文档中,术语“引导程序(Boot program)”被泛指为初始化系统状态并执行另一个被称为客户端程序(Client program)的软件组件。引导程序的例子包括:固件、引导加载程序和虚拟机监视器。客户端程序的例子包括:引导加载程序、虚拟机监视器、操作系统和特殊用途程序。一个软件可能同时是客户端程序和引导程序(例如虚拟机监视器)。

本规范,即设备树规范(Devicetree Specification,简称DTSpec),提供了一个完整的引导程序到客户端程序接口定义,并结合了促进开发各种系统的最低系统要求。本规范针对嵌入式系统(Embedded Systems)的要求。嵌入式系统通常由为执行一组固定、特定任务而定制设计的系统硬件、操作系统和应用软件组成。这与通用计算机不同,通用计算机设计为可由用户通过各种软件和输入/输出(I/O)设备进行定制。嵌入式系统的其他特征可能包括:

• 一组固定的输入/输出设备,可能为应用程序高度定制
• 为尺寸和成本优化的系统板
• 有限的用户界面
• 资源限制,如有限的内存和有限的非易失性存储
• 实时约束
• 使用各种操作系统,包括Linux、实时操作系统和定制或专有操作系统

本文档的组织结构:

• 第1章介绍了DTSpec所规范的架构。
• 第2章介绍了设备树(Devicetree)概念,并描述了其逻辑结构和标准属性。
• 第3章规定了DTSpec兼容设备树所需的基本设备节点(Device Nodes)集的定义。
• 第4章描述了某些类别的设备和特定设备类型的设备绑定(Device Bindings)。
• 第5章规定了设备树的DTB编码。
• 第6章规定了DTS源语言。

1.2 和IEEE 1275与ePAR的关系

DTSpec与IEEE 1275开放固件标准(IEEE Standard for Boot (Initialization Configuration) Firmware: Core Requirements and Practices [IEEE1275])有松散的关联。原始的IEEE 1275规范及其衍生物,如CHRP和PAPR,解决了通用计算机的问题,例如单一版本的操作系统如何在同一个系列中的几台不同的计算机上工作,以及从用户安装的输入/输出(I/O)设备加载操作系统的问题。

由于嵌入式系统的本质,开放的通用计算机所面临的一些问题并不适用。IEEE 1275规范中从DTSpec中省略的显著特性包括:

• 插件式设备驱动程序(Plug-in device drivers)
• FCode
• 基于Forth的可编程开放固件用户界面(The programmable Open Firmware user interface based on Forth)
• FCode调试(FCode debugging)
• 操作系统调试(Operating system debugging)

从IEEE 1275保留下来的是设备树(devicetree)架构中的概念,通过该架构,引导程序可以描述并传递系统硬件信息给客户端程序,从而消除了客户端程序必须有系统硬件硬编码描述的需要。

本规范部分取代了ePAPR规范。ePAPR文档说明了Power ISA如何使用设备树,并涵盖了一般概念以及Power ISA特定的绑定。本文档的文本源自ePAPR,但要么删除了架构特定的绑定,要么将其移至附录中。

1.3 32-bit和64-bit的支持

DTSpec支持具有32位和64位寻址能力的CPU。在适用的情况下,DTSpec的各个部分描述了32位和64位寻址的任何要求或考虑事项。

1.4 术语定义

专业名词 解释
AMP Asymmetric Multiprocessing,非对称多处理
Book III-E Embedded Environment,嵌入式环境
boot program 引导程序,用于初始化系统状态并执行客户端程序的软件组件
client program 客户端程序,通常包含应用程序或操作系统软件
cell 信息单元,由32位组成
DMA Direct Memory Access,直接内存访问
DTB Devicetree Blob,设备树二进制大对象
DTC Devicetree Compiler,设备树编译器
DTS Devicetree Syntax,设备树语法
effective address 处理器存储访问或分支指令计算出的记忆体地址
physical address 处理器用于访问外部设备的地址,通常是内存控制器
Power ISA Power Instruction Set Architecture,Power指令集架构
interrupt specifier 中断标识符,描述中断的属性值
secondary CPU 次要CPU,不属于引导CPU但属于客户端程序的CPU
SMP Symmetric Multiprocessing,对称多处理
SoC System on a Chip,片上系统
unit address 单元地址,指定节点在父节点地址空间中的地址
quiescent CPU 静止状态的CPU,不能干扰其他CPU的正常运行,也不能被影响

二、设备树

2.1 概述

DTSpec指定了一个称为设备树(devicetree)的结构来描述系统硬件。引导程序(boot program)将设备树加载到客户端程序(client program)的内存中,并向客户端传递指向设备树的指针。

本章描述了设备树的逻辑结构,并指定了一组用于描述设备节点(device nodes)的基础属性。第3章指定了DTSpec兼容设备树所需的特定设备节点。第4章描述了DTSpec定义的设备绑定(device bindings)——表示特定设备类型或设备类别的要求。第5章描述了设备树在内存中的编码方式。

设备树是一种树形数据结构,其中的节点描述系统中的设备。每个节点都有属性/值对,描述所代表设备的特征。除了根节点没有父节点外,每个节点都恰好有一个父节点。

在此上下文中,设备可以是实际的硬件设备,例如 UART。它可能是硬件设备的一部分,例如 TPM 中的随机数生成器。它也可以是通过虚拟化提供的设备,例如提供访问连接到远程 CPU 的 I2C 设备的协议。设备可能包括由在更高权限级别运行的固件或远程处理器实现的功能。设备树中的节点没有要求必须是物理硬件设备,但通常它们与物理硬件设备有一定的关联。节点不应为操作系统(OS)或项目特定目的而设计。它们应该描述任何操作系统或项目都可以实现的东西。

设备树通常用于描述客户端程序不一定能动态检测到的设备。例如,PCI 架构使客户端能够探测和检测连接的设备,因此可能不需要描述 PCI 设备的设备树节点。然而,设备节点通常用于描述系统中的 PCI 主桥设备。如果桥接器不能通过探测检测到,则需要此节点,否则是可选的。此外,引导加载程序可能会进行 PCI 探测,并生成包含其扫描结果的设备树,以便传递给操作系统。

例子:图 2.1 展示了一个简单的设备树(devicetree)的示例表示,它几乎足够完整,可以启动一个简单的操作系统,其中描述了平台类型、CPU、内存和一个单一的UART。设备节点(Device nodes)在每个节点内显示了属性和值。

2.2 设备树结构

2.2.1 节点名称

设备树(devicetree)中的每个节点都按照以下约定命名:

node-name@unit-address

节点名称(node-name)部分指定了节点的名称。它的长度应为1到31个字符,并且只能由表2.1中的字符集组成。

图片[1] - 嵌入式系统内核镜像相关(四) - 宋马
节点名称(node-name)应以小写或大写字母开头,并应描述设备的一般类别。

名称中的单元地址(unit-address)部分是特定于节点所在的总线类型。它由一个或多个来自表2.1字符集的ASCII字符组成。单元地址必须与节点的reg属性中指定的第一个地址匹配。如果节点没有reg属性,则必须省略@unit-address,并且仅节点名称(node-name)就能将该节点与树中同一层级的其他节点区分开来。特定总线的绑定(binding)可能对reg和单元地址(unit-address)的格式规定了额外的、更具体的要求。

在没有@unit-address的节点名称(node-name)的情况下,节点名称应与树中同一层级的任何属性名称唯一。

根节点没有节点名称(node-name)或单元地址(unit-address)。它由正斜杠(/)标识。
图片[2] - 嵌入式系统内核镜像相关(四) - 宋马
在图2.2中:

名为cpu的节点通过它们的单元地址值01来区分。

名为ethernet的节点通过它们的单元地址值fe002000fe003000来区分。

2.2.2 通用名称推荐

节点的名称应该具有一定的通用性,反映设备的功能而不是其精确的编程模型。如果合适,名称应该是以下选择之一:
图片[3] - 嵌入式系统内核镜像相关(四) - 宋马

2.2.3 路径名称

在设备树(devicetree)中,可以通过指定从根节点到目标节点的完整路径,通过所有后代节点,来唯一标识一个节点。

指定设备路径的约定是:

/node-name-1/node-name-2/node-name-N

例如,在图2.2中,到 CPU #1 的设备路径将是:

/cpus/cpu@1

到根节点的路径是 /
如果到节点的完整路径是明确的,可以省略单元地址(unit address)。
如果客户端程序遇到一个模糊的路径,其行为是未定义的。

2.2.4 属性

设备树(devicetree)中的每个节点都有描述节点特征的属性。属性由名称和值组成。

2.2.4.1 属性名

属性名称是由表 2.2 中显示的字符组成的1到31个字符的字符串。

图片[4] - 嵌入式系统内核镜像相关(四) - 宋马
非标准属性名称应指定一个唯一的字符串前缀,例如股票代码,以识别定义该属性的公司或组织的名称。例如:

fsl,channel-fifo-len
ibm,ppc-interrupt-server#s
linux,network-index

2.2.4.2 属性值

属性值是一个包含与属性相关联信息的零个或多个字节的数组。

如果属性用于传递真-假信息,它可能具有空值。在这种情况下,属性的存在或缺失本身就具有足够的描述性。

表 2.3 描述了 DTSpec 定义的基本值类型的集合。
图片[5] - 嵌入式系统内核镜像相关(四) - 宋马
图片[6] - 嵌入式系统内核镜像相关(四) - 宋马
图片[7] - 嵌入式系统内核镜像相关(四) - 宋马

2.3 标准属性

DTSpec指定了一套设备节点的标准属性。这些属性将在本节中详细描述。
由DTSpec定义的设备节点(参见第3章)可能会对使用标准属性提出额外的要求或限制。第4章描述了特定设备的表示形式,也可能指定额外的要求。

注意:本文档中所有设备树节点的示例都使用DTS(Devicetree Source)格式来指定节点和属性。

2.3.1 compatible

属性名称:compatible
值类型:<stringlist>
描述:
compatible属性值由一个或多个字符串组成,用于定义设备的特定编程模型。客户端程序应使用此字符串列表来选择设备驱动程序。属性值由从最具体到最通用的空字符终止字符串拼接而成。它们允许设备表达其与一系列类似设备的兼容性,从而可能使单个设备驱动程序能够匹配多个设备。

推荐的格式为"manufacturer,model",其中制造商是一个描述制造商名称的字符串(例如股票代码),型号则指定型号编号。

compatible字符串应仅包含小写字母、数字和破折号,并且必须以字母开头。通常,单个逗号仅在供应商前缀后使用。不应使用下划线。

示例:
compatible = "fsl,mpc8641", "ns16550";
在此示例中,操作系统会首先尝试找到支持fsl,mpc8641的设备驱动程序。如果未找到驱动程序,它将尝试找到支持更通用的ns16550设备类型的驱动程序。

2.3.2 model

属性名称:model
值类型:<string>
描述:
model属性值是一个<string>,用于指定设备的制造商型号编号。
推荐的格式为:"manufacturer,model",其中manufacturer是一个描述制造商名称的字符串(例如股票代码),model则指定型号编号。

示例:
model = "fsl,MPC8349EMITX"

2.3.3 phandle

属性名称:phandle
值类型:<u32>
描述:
phandle 属性指定一个在设备树中唯一的数值标识符,用于标识一个节点。该属性值被其他需要引用与该属性相关联节点的节点所使用。

请参阅以下设备树片段:
图片[8] - 嵌入式系统内核镜像相关(四) - 宋马
定义了一个值为 1 的phandle。另一个设备节点可以通过值为1phandle引用pic节点:
图片[9] - 嵌入式系统内核镜像相关(四) - 宋马
注意:在某些旧版本的设备树中,可能会遇到一个已废弃的属性形式,名为 linux,phandle。为了兼容性,如果不存在 phandle 属性,客户端程序可能需要支持 linux,phandle。这两个属性的含义和用途是相同的。

注意:在DTS(见附录A)中的大多数设备树不会显式包含phandle属性。DTC工具会在将DTS编译为二进制DTB格式时自动插入phandle属性。

2.3.4 statuts

属性名称:status
值类型:<string>
描述:
status属性指示设备的运行状态。如果不存在status属性,则应将其视为存在且值为"okay"。有效值在表2.4中列出并定义。

2.3.5 #address-cells和#size-cells

属性名称:#address-cells, #size-cells
值类型:<u32>
描述:
#address-cells#size-cells属性可用于设备树层次结构中具有子节点的任何设备节点,并描述如何寻址子设备节点。#address-cells属性定义了子节点的reg属性中地址字段所使用的<u32>单元格数量。#size-cells属性定义了子节点的reg属性中大小字段所使用的<u32>单元格数量。

#address-cells#size-cells属性不会从设备树的祖先节点继承,必须显式定义。

符合DTSpec的引导程序应在所有具有子节点的节点上提供#address-cells#size-cells

如果缺失,客户端程序应假设#address-cells的默认值为 2,#size-cells的默认值为1

请参阅以下设备树片段:
图片[10] - 嵌入式系统内核镜像相关(四) - 宋马
在此示例中,soc节点的#address-cells#size-cells属性均设置为1。此设置表明,表示此节点子节点的地址需要一个单元格,表示大小也需要一个单元格。

串行设备的reg属性必然遵循父节点(soc)中设置的规范——地址由一个单元格表示(0x4600),大小由一个单元格表示(0x100)。

2.3.6 reg

属性名称:reg
属性值:<prop-encoded-array>,编码为任意数量的(地址,长度)对。
描述:
reg 属性描述设备在其父总线定义的地址空间内的资源地址。通常,这表示内存映射 I/O 寄存器块的偏移量和长度,但在某些总线类型上可能有不同的含义。根节点定义的地址空间中的地址是 CPU 的实际地址。

该值是一个 <prop-encoded-array>,由任意数量的地址和长度对 <address length> 组成。指定地址和长度所需的 <u32> 单元格数量是总线特定的,由设备节点的父节点中的 #address-cells#size-cells 属性指定。如果父节点将 #size-cells 的值指定为 0,则 reg 的值中应省略长度字段。

示例:
假设片上系统(SoC)中的一个设备有两个寄存器块,一个位于SoC的偏移量 0x3000 处的32字节块,另一个位于偏移量 0xFE00 处的256字节块。reg 属性将按照以下方式编码(假设 #address-cells#size-cells 的值为 1):
reg = <0x3000 0x20 0xFE00 0x100>

2.3.7 virtual-reg

属性名称:virtual-reg
值类型:<u32>
描述:
virtual-reg 属性指定一个有效的地址,该地址映射到设备节点的 reg 属性中指定的第一个物理地址。此属性使引导程序能够为客户端程序提供已经设置好的虚拟到物理的映射关系。

2.3.8 ranges

属性名称:ranges
值类型:<empty><prop-encoded-array>,编码为任意数量的(子总线地址,父总线地址,长度)三元组。
描述:
ranges 属性提供了一种定义总线地址空间(子地址空间)与总线节点父级地址空间(父地址空间)之间映射或转换的方法。
ranges 属性值的格式为任意数量的(子总线地址,父总线地址,长度)三元组:

子总线地址(child-bus-address):是子总线地址空间内的一个物理地址。表示该地址所需的单元格数量取决于总线类型,可以从该节点(即包含 ranges 属性的节点)的 #address-cells 属性中确定。
父总线地址(parent-bus-address):是父总线地址空间内的一个物理地址。表示父地址所需的单元格数量取决于总线类型,可以从定义父地址空间的节点的 #address-cells 属性中确定。
长度(length):指定子地址空间中范围的大小。表示大小所需的单元格数量可以从该节点(即包含 ranges 属性的节点)的 #size-cells 属性中确定。

如果该属性被定义为 <empty> 值,则表示父地址空间和子地址空间是相同的,不需要进行地址转换。
如果在总线节点中未定义 ranges 属性,则假定该节点的子节点与父地址空间之间不存在映射关系。

地址转换示例

soc 节点定义了一个 ranges 属性,值为:
<0x0 0xe0000000 0x00100000>;

此属性值指定,在 1024KB 的地址空间范围内,子节点的物理地址 0x0 映射到父节点的物理地址 0xe0000000。通过这种映射,串行设备节点可以通过地址 0xe0004600 进行访问,即 0x4600(由 reg 属性指定的偏移量)加上 ranges 属性中指定的 0xe0000000 映射。

2.3.9 dma-ranges

属性名称:dma-ranges
值类型:<empty><prop-encoded-array>,编码为任意数量的(子总线地址,父总线地址,长度)三元组。
描述:
dma-ranges 属性用于描述一个内存映射总线的直接内存访问(DMA)结构,该总线的设备树父节点可以通过来自该总线的 DMA 操作进行访问。它提供了一种定义总线物理地址空间与总线父节点物理地址空间之间映射或转换的方法。
dma-ranges 属性值的格式为任意数量的(子总线地址,父总线地址,长度)三元组。每个三元组描述了一个连续的 DMA 地址范围。

子总线地址(child-bus-address):是子总线地址空间内的一个物理地址。表示该地址所需的单元格数量取决于总线类型,可以从该节点(即包含 dma-ranges 属性的节点)的 #address-cells 属性中确定。
父总线地址(parent-bus-address):是父总线地址空间内的一个物理地址。表示父地址所需的单元格数量取决于总线类型,可以从定义父地址空间的节点的 #address-cells 属性中确定。
长度(length):指定子地址空间中范围的大小。表示大小所需的单元格数量可以从该节点(即包含 dma-ranges 属性的节点)的 #size-cells 属性中确定。

2.3.10 dma-coherent

属性名称:dma-coherent
值类型:<empty>
描述:
对于默认情况下 I/O 操作是非一致性的架构,dma-coherent 属性用于指示设备能够执行一致性的 DMA(直接内存访问)操作。某些架构默认支持一致性 DMA,这种情况下该属性不适用。

2.3.11 dma-noncoherent

属性名称:dma-noncoherent
值类型:<empty>
描述:
对于默认情况下 I/O 操作是一致性的架构,dma-noncoherent 属性用于指示设备支持一致性 DMA(直接内存访问)操作。某些架构默认支持非一致性 DMA,这种情况下该属性不适用。

2.3.12 name(已废弃)

属性名称:name
值类型:<string>
描述:
name 属性是一个字符串,用于指定节点的名称。此属性已被废弃,不推荐使用。然而,它可能仍然出现在某些旧版本的非 DTSpec 兼容设备树中。操作系统应根据节点名称中的节点名部分(见第 2.2.1 节)来确定节点的名称。

2.3.13 device_type(已废弃)

属性名称:device_type
值类型:<string>
描述:
device_type 属性在 IEEE 1275 中用于描述设备的 FCode 编程模型。由于 DTSpec(设备树规范)中没有 FCode,因此该属性的新用法已被废弃,仅应包含在 cpumemory 节点中,以兼容基于 IEEE 1275 的设备树。

2.4 中断和中断映射

DTSpec采用了Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9中指定的中断树(interrupt tree)模型来表示中断。在设备树中,存在一个逻辑中断树,用于表示平台硬件中中断的层次结构和路由关系。虽然通常被称为中断树,但从技术上讲,它更像一个有向无环图(Directed Acyclic Graph)。

中断源(interrupt source)与中断控制器(interrupt controller)的物理连接在设备树中通过 interrupt-parent 属性表示。表示中断生成设备(interrupt-generating devices)的节点包含一个 interrupt-parent 属性,该属性的值是一个 phandle,指向该设备的中断被路由到的设备,通常是中断控制器。如果一个中断生成设备没有 interrupt-parent 属性,则假设其中断父节点(interrupt parent)是其设备树中的父节点(devicetree parent)。

每个中断生成设备都包含一个 interrupts 属性,其值描述了该设备的一个或多个中断源。每个中断源通过称为中断说明符(interrupt specifier)的信息来表示。中断说明符的格式和含义是中断域特定的,即它依赖于其中断域(interrupt domain)根节点上的属性。#interrupt-cells 属性由中断域的根节点使用,用于定义编码中断说明符所需的 <u32> 值的数量。例如,对于一个Open PIC中断控制器,中断说明符需要两个32bit值,包括中断编号中断的电平/触发方式信息。

中断域是解释中断说明符的上下文。域的根节点可以是以下两种类型之一:

中断控制器:中断控制器是一个物理设备,需要一个驱动程序来处理通过它的中断。它也可以级联到另一个中断域。设备树中某个节点上的 interrupt-controller 属性表明该节点是一个中断控制器。
中断中继节点(Interrupt Nexus):中断中继节点定义了一个中断域与另一个中断域之间的转换。这种转换基于特定于域和总线的信息。域之间的转换通过 interrupt-map 属性完成。例如,PCI 控制器设备节点可以是一个中断中继节点,它定义了从 PCI 中断命名空间(INTA、INTB 等)到具有中断请求(IRQ)编号的中断控制器的转换。

中断树的根节点是在遍历中断树时到达一个没有 interrupts 属性的中断控制器节点时确定的,因此它没有显式的中断父节点。

请参见图 2.3,它展示了带有中断父节点关系的设备树的图形化表示。它既显示了设备树的自然结构,也显示了每个节点在逻辑中断树中的位置。
图片[11] - 嵌入式系统内核镜像相关(四) - 宋马
在图 2.3 所示的示例中:

open-pic 中断控制器是中断树的根节点。
中断树的根节点有三个子节点——这些设备的中断直接路由到 open-pic

device1
PCI 主机控制器
GPIO 控制器

存在三个中断域;分别以 open-pic 节点、PCI 主机桥节点和 GPIO 控制器节点为根节点。
有两个中继节点(nexus nodes);一个位于 PCI 主机桥,另一个位于 GPIO 控制器。

2.4.1 Interrupt Generating Devices的属性

2.4.1.1 interrupt

属性名称:interrupts
值类型:<prop-encoded-array>,编码为任意数量的中断说明符
描述:
设备节点的 interrupts 属性定义了该设备生成的中断或多个中断。interrupts 属性的值由任意数量的中断说明符组成,中断说明符的格式由中断域根节点的绑定定义。interrupts 属性可以被 interrupts-extended 属性覆盖,通常两者中应仅使用其中一个。

示例:
在兼容 Open PIC 的中断域中,一个常见的中断说明符定义包含两个单元格:中断编号和电平/触发方式信息。请参阅以下示例,它定义了一个单一的中断说明符,其中断编号为 0xA,电平/触发方式编码为 8
interrupts = <0xA 8>

2.4.1.2 interrupt-parent

属性名称:interrupt-parent
值类型:<phandle>
描述:
由于中断树的节点层次结构可能与设备树不匹配,interrupt-parent 属性可用于明确指定中断父节点。其值是中断父节点的 phandle。如果设备中缺少此属性,则假设其中断父节点是其设备树中的父节点。

2.4.1.3 interrupts-extended

属性名称:interrupts-extended
值类型:<phandle> <prop-encoded-array>
描述:
interrupts-extended 属性列出设备生成的中断。当设备连接到多个中断控制器时,应使用 interrupts-extended 而不是 interrupts,因为它会为每个中断说明符编码一个父节点的 phandle

示例:
此示例展示了如何使用 interrupts-extended 属性描述一个具有两个中断输出且分别连接到两个不同中断控制器的设备。pic 是一个中断控制器,其 #interrupt-cells 说明符为 2,而 gic 是一个中断控制器,其 #interrupt-cells 说明符为 1。
interrupts-extended = <&pic 0xA 8>, <&gic 0xda>

interruptsinterrupts-extended 属性是互斥的。设备节点应使用其中一个,而不是同时使用两者。只有在需要与不支持 interrupts-extended 的软件保持兼容性时,才允许同时使用这两个属性。如果同时存在 interrupts-extendedinterrupts,则以 interrupts-extended 为准。

2.4.2 Interrupt Controllers的属性

2.4.2.1 #interrupt-cells

属性名称:#interrupt-cells
值类型:<u32>
描述:
#interrupt-cells 属性定义了在中断域中编码一个中断说明符所需的单元格数量。

2.4.2.2 interrupt-controller

属性名称:interrupt-controller
值类型:<empty>
描述:
设备树节点中存在 interrupt-controller 属性表明该节点是一个中断控制器节点。

2.4.3 Interrupt中继(Nexus)属性

一个中断中继节点(interrupt nexus node)必须具有 #interrupt-cells 属性。

2.4.3.1 interrupt-map

属性名称:interrupt-map
值类型:<prop-encoded-array>,编码为任意数量的中断映射条目。
描述:
interrupt-map 是一个位于中断中继节点(nexus node)上的属性,用于连接一个中断域与其父中断域的集合,并指定子域中的中断说明符如何映射到各自的父域中。

中断映射表中的每一行是一个映射条目,包含五个组成部分:子节点单元地址、子中断说明符、中断父节点、父节点单元地址、父中断说明符。

子节点单元地址(child unit address)
正在被映射的子节点的单元地址。指定此地址所需的 32 位单元格数量由子节点所在总线节点的 #address-cells 属性决定。
子中断说明符(child interrupt specifier)
正在被映射的子节点的中断说明符。指定此部分所需的 32 位单元格数量由包含 interrupt-map 属性的中继节点的 #interrupt-cells 属性决定。
中断父节点(interrupt-parent)
一个 <phandle> 值,指向子域正在被映射到的中断父节点。
父节点单元地址(parent unit address)
在中断父域中的单元地址。指定此地址所需的 32 位单元格数量由 interrupt-parent 字段所指向的节点的 #address-cells 属性决定。
父中断说明符(parent interrupt specifier)
父域中的中断说明符。指定此部分所需的 32 位单元格数量由 interrupt-parent 字段所指向的节点的 #interrupt-cells 属性决定。

通过将单元地址/中断说明符对与 interrupt-map 中的子组件进行匹配,来执行中断映射表的查找操作。由于单元中断说明符中某些字段可能并不相关,因此在进行查找之前会应用一个掩码。该掩码在 interrupt-map-mask 属性中定义(见第 2.4.3 节)。

注意:子节点和中断父节点都必须定义了 #address-cells#interrupt-cells 属性。如果不需要单元地址组件,则应显式将 #address-cells 定义为零。

2.4.3.2 interrupt-map-mask

属性名称:interrupt-map-mask
值类型:<prop-encoded-array>,编码为一个位掩码
描述:
interrupt-map-mask 属性是为中断树中的中断中继节点(nexus node)指定的。该属性定义了一个掩码,该掩码会与在 interrupt-map 属性指定的表中查找的传入单元中断说明符进行按位与(AND)操作。

2.4.3.3 interrupt-cells

属性名称:#interrupt-cells
值类型:<u32>
描述:
#interrupt-cells 属性定义了在中断域中编码一个中断说明符所需的单元格数量。

2.4.4 Interrupt映射的例子

以下展示了包含一个 PCI 总线控制器和一个样本中断映射的设备树片段的表示方法,用于描述两个 PCI 插槽(IDSEL 为 0x110x12)的中断路由。插槽 1 和插槽 2 的 INTA、INTB、INTC 和 INTD 引脚连接到 Open PIC 中断控制器。
图片[12] - 嵌入式系统内核镜像相关(四) - 宋马
图片[13] - 嵌入式系统内核镜像相关(四) - 宋马
一个 Open PIC 中断控制器被表示出来,并通过 interrupt-controller 属性被识别为一个中断控制器。

中断映射表中的每一行包含五个部分:子节点单元地址和中断说明符,它们被映射到一个具有指定父节点单元地址和中断说明符的中断父节点。

例如,中断映射表的第一行指定了插槽 1 的 INTA 的映射。该行的组成部分如下:

子节点单元地址:0x8800 0 0
子中断说明符:1
中断父节点:&open-pic
父节点单元地址:(为empty,因为 Open PIC 节点的 #address-cells 值为 <0>
父中断说明符:2 1

子节点单元地址是 <0x8800 0 0>。这个值由三个 32 位单元格编码而成,这是由 PCI 控制器的 #address-cells 属性值(3)决定的。这三个单元格按照 PCI 总线的绑定描述了 PCI 地址。

编码包括总线号(0x0 << 16)、设备号(0x11 << 11)和功能号(0x0 << 8)。

子中断说明符是 <1>,它按照 PCI 绑定描述了 INTA。它占用一个 32 位单元格,这是由 PCI 控制器的 #interrupt-cells 属性值(1)决定的,PCI 控制器是子中断域。
中断父节点由一个 phandle 指定,指向插槽的中断父节点,即 Open PIC 中断控制器。
父节点没有单元地址,因为父中断域(Open PIC 节点)的 #address-cells 值为 <0>
父中断说明符是 <2 1>。表示中断说明符所需的单元格数量(两个单元格)由中断父节点(Open PIC 节点)的 #interrupt-cells 属性决定。

<2 1> 是由 Open PIC 中断控制器的设备绑定指定的(见第 4.5 节)。值 <2> 指定了中断控制器上与 INTA 连接的物理中断源编号。值 <1> 指定了电平/触发方式编码。

在本例中,interrupt-map-mask 属性的值为 <0xf800 0 0 7>。在查找中断映射表之前,会将此掩码应用于子节点单元中断说明符。为了查找 IDSEL 0x12(插槽 2)的 INTB 的 Open PIC 中断源编号,功能号为 0x3,将执行以下步骤:

子节点单元地址和中断说明符形成值 <0x9300 0 0 2>

地址的编码包括总线号(0x0 << 16)、设备号(0x12 << 11)和功能号(0x3 << 8)。
中断说明符为 2,这是按照 PCI 绑定对 INTB 的编码。

应用 interrupt-map-mask<0xf800 0 0 7>,得到结果 <0x9000 0 0 2>
在中断映射表中查找该结果,它映射到父中断说明符 <4 1>

2.5 中继(Nexus)节点与说明符(specifier)映射

2.5.1 中继(Nexus)节点属性

一个中继节点(nexus node)应当具有一个 #<specifier>-cells 属性,其中 <specifier> 是某种说明符空间,例如 “gpio”、“clock”、“reset” 等。

2.5.1.1 <specifier>-map

属性名称:<specifier>-map
值类型:<prop-encoded-array>,编码为任意数量的说明符映射条目。
描述:
<specifier>-map 是一个位于中继节点(nexus node)中的属性,用于连接一个说明符域与其父说明符域的集合,并描述子域中的说明符如何映射到各自的父域中。

映射表中的每一行是一个映射条目,包含三个组成部分:子说明符、说明符父节点和父说明符。

子说明符(child specifier)
正在被映射的子节点的说明符。指定此部分所需的 32 位单元格数量由包含 <specifier>-map 属性的中继节点的 #<specifier>-cells 属性决定。
说明符父节点(specifier parent)
一个 <phandle> 值,指向子域正在被映射到的说明符父节点。
父说明符(parent specifier)
父域中的说明符。指定此部分所需的 32 位单元格数量由说明符父节点的 #<specifier>-cells 属性决定。

通过将说明符与映射表中的子说明符进行匹配来执行查找操作。由于说明符中某些字段可能并不相关或需要修改,因此在进行查找之前会应用一个掩码。该掩码在 <specifier>-map-mask 属性中定义(见第 2.5.1 节)。

同样地,当说明符被映射时,单元说明符中的一些字段可能需要保持不变,并从子节点传递到父节点。在这种情况下,可以指定一个 <specifier>-map-pass-thru 属性(见第 2.5.1 节),以对子说明符应用掩码,并将匹配的位复制到父单元说明符中。

2.5.1.2 <specifier>-map-mask

属性名称:<specifier>-map-mask
值类型:<prop-encoded-array>,编码为一个位掩码
描述:
可以为中继节点(nexus node)指定 <specifier>-map-mask 属性。该属性定义了一个掩码,该掩码会与在 <specifier>-map 属性指定的表中查找的子节点单元说明符进行按位与(AND)操作。如果未指定此属性,则假设掩码为所有位都设置为 1 的掩码。

2.5.1.3 <specifier>-map-pass-thru

属性名称:<specifier>-map-pass-thru
值类型:<prop-encoded-array>,编码为一个位掩码
描述:
可以为中继节点(nexus node)指定 <specifier>-map-pass-thru 属性。该属性定义了一个掩码,该掩码应用于在 <specifier>-map 属性指定的表中查找的子节点单元说明符。子节点单元说明符中与掩码匹配的位将被复制到父说明符中。如果未指定此属性,则假设掩码为没有位被设置的掩码。

2.5.1.4 #<specifier>-cells

属性名称:#<specifier>-cells
值类型:<u32>
描述:
#<specifier>-cells 属性定义了为一个域编码一个说明符所需的单元格数量。

2.5.2 说明符映射的例子

以下展示了包含两个 GPIO 控制器和一个样本说明符映射的设备树片段的表示方法,用于描述通过主板上的连接器将两个控制器上的几个 GPIO 路由到设备的 GPIO 连接关系。扩展设备节点位于连接器节点的一侧,而包含两个 GPIO 控制器的 SoC 位于连接器的另一侧。

图片[14] - 嵌入式系统内核镜像相关(四) - 宋马
图片[15] - 嵌入式系统内核镜像相关(四) - 宋马
gpio-map 表中的每一行包含三个部分:子节点单元说明符,它被映射到一个 GPIO 控制器节点,并带有父说明符。

例如,说明符映射表的第一行指定了连接器的 GPIO 0 的映射。该行的组成部分如下:

子说明符:0 0
说明符父节点:&soc_gpio1
父说明符:1 0

子说明符是 <0 0>,它指定了连接器的 GPIO 0,标志字段为 0。这需要两个 32 位单元格,这是由连接器节点(子说明符域)的 #gpio-cells 属性决定的。
说明符父节点由一个 phandle 指定,指向连接器的说明符父节点,即 SoC 中的第一个 GPIO 控制器。
父说明符是 <1 0>。表示 GPIO 说明符所需的单元格数量(两个单元格)由说明符父节点(soc_gpio1 节点)的 #gpio-cells 属性决定。

<1 0> 是由 GPIO 控制器的设备绑定指定的。值 <1> 指定了 GPIO 控制器上的 GPIO 引脚编号,连接器的 GPIO 0 与此引脚相连。值 <0> 指定了标志(如低电平有效、高电平有效等)。

在本例中,gpio-map-mask 属性的值为 <0xf 0>。在查找 gpio-map 表之前,会将此掩码应用于子节点单元说明符。同样地,gpio-map-pass-thru 属性的值为 <0x0 0x1>。在将子节点单元说明符映射到父节点单元说明符时,会应用此掩码。此掩码中设置的任何位都会从父节点单元说明符中清除,并从子节点单元说明符复制到父节点单元说明符。

为了查找扩展设备的 reset-gpios 属性中 GPIO 2 的连接器说明符源编号,将执行以下步骤:

子说明符形成值 <2 GPIO_ACTIVE_LOW>

该说明符按照 GPIO 绑定对 GPIO 2 进行编码,并设置为低电平有效标志。

gpio-map-mask<0xf 0x0> 与子说明符进行按位与(AND)操作,结果为 <0x2 0>
gpio-map 表中查找该结果,它映射到父说明符 <3 0>&soc_gpio1phandle
gpio-map-pass-thru<0x0 0x1> 取反后与 gpio-map 表中找到的父说明符进行按位与操作,结果为 <3 0>。子说明符与 gpio-map-pass-thru 掩码进行按位与操作,形成 <0 GPIO_ACTIVE_LOW>,然后与清空后的父说明符 <3 0> 进行按位或(OR)操作,最终结果为 <3 GPIO_ACTIVE_LOW>
说明符 <3 GPIO_ACTIVE_LOW> 被附加到映射的 phandle &soc_gpio1 上,最终结果为 <&soc_gpio1 3 GPIO_ACTIVE_LOW>

三、设备节点规格要求

3.1 基本设备节点类型

以下各节指定了符合 DTSpec 的设备树所需的基设备节点的要求。

所有设备树都必须有一个根节点,并且所有设备树的根节点必须包含以下节点:

一个 /cpus 节点
至少一个 /memory 节点

3.2 根节点

设备树有一个单一的根节点,所有其他设备节点都是其后代。根节点的完整路径是 /
图片[16] - 嵌入式系统内核镜像相关(四) - 宋马
图片[17] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但均为可选属性。

3.3 /aliases节点

设备树可以有一个别名节点(/aliases),用于定义一个或多个别名属性。别名节点应位于设备树的根节点,并具有节点名 /aliases
/aliases 节点的每个属性定义了一个别名。属性名称指定别名的名称,属性值指定设备树中一个节点的完整路径。例如,属性 serial0 = "/simple-bus@fe000000/serial@llc500" 定义了别名 serial0
别名名称应为小写文本字符串,长度为 1 到 31 个字符,字符集如下:

别名的值是一个设备路径,编码为字符串。该值表示一个节点的完整路径,但路径不必指向叶节点。

客户端程序可以使用别名属性名称来引用完整设备路径,作为其字符串值的一部分或全部。客户端程序在将字符串视为设备路径时,应检测并使用别名。

示例:

aliases {
    serial0 = "/simple-bus@fe000000/serial@llc500";
    ethernet0 = "/simple-bus@fe000000/ethernet@31c000";
};

给定别名 serial0,客户端程序可以查看 /aliases 节点,并确定该别名指向的设备路径为 /simple-bus@fe000000/serial@llc500

3.4 /memory节点

所有设备树都需要一个内存设备节点,用于描述系统的物理内存布局。如果一个系统有多个内存范围,可以创建多个内存节点,或者在单个内存节点的 reg 属性中指定这些范围。

节点名称(unit-name)(见第 2.2.1 节)的单元名称部分应为 memory

客户端程序可以使用其选择的任何存储属性来访问未被任何内存保留区域(见第 5.3 节)覆盖的内存。然而,在更改用于访问实际页面的存储属性之前,客户端程序负责执行架构和实现所要求的操作,可能包括从缓存中清除该实际页面。引导程序负责确保,在不采取与存储属性更改相关的任何操作的情况下,客户端程序可以安全地访问所有内存(包括被内存保留区域覆盖的内存),作为 WIMG = 0b001x。即:

不要求写通(not Write Through Required)
不禁止缓存(not Caching Inhibited)
内存一致性(Memory Coherence)
要求为非保护或保护(Required either not Guarded or Guarded)

如果支持 VLE 存储属性,则为 VLE=0

3.4.1 /memory节点和UEFI

当通过UEFI引导时,系统内存映射是通过UEFI第 7.2 节中定义的 GetMemoryMap() UEFI引导时服务获取的,如果存在,则操作系统必须忽略任何 /memory 节点。

3.4.2 /memory例子

假设有一个 64 位的 Power 系统,其物理内存布局如下:

内存:起始地址 0x0,长度 0x80000000(2GB)
内存:起始地址 0x100000000,长度 0x100000000(4GB)

在假设 #address-cells = <2>#size-cells = <2> 的情况下,内存节点可以定义如下:

示例 #1

memory@0 {
    device_type = "memory";
    reg = <0x000000000 0x00000000 0x00000000 0x80000000
           0x000000001 0x00000000 0x00000001 0x00000000>;
};

示例 #2

memory@0 {
    device_type = "memory";
    reg = <0x000000000 0x00000000 0x00000000 0x80000000>;
};

memory@100000000 {
    device_type = "memory";
    reg = <0x000000001 0x00000000 0x00000001 0x00000000>;
}

reg 属性用于定义两个内存范围的地址和大小。2 GB 的 I/O 区域被跳过。注意,根节点的 #address-cells#size-cells 属性值为 2,这意味着需要两个 32 位单元格来定义内存节点的 reg 属性中的地址和长度。

3.5 /reserved-memory节点

保留内存被指定为 /reserved-memory 节点下的一个子节点。操作系统应将保留内存排除在正常使用之外。可以创建描述特定保留(从正常使用中排除)内存区域的子节点。这些内存区域通常被设计为供各种设备驱动程序进行特殊用途。

每个内存区域的参数可以通过以下节点编码到设备树中:

3.5.1 /reserved-memory parent节点

图片[18] - 嵌入式系统内核镜像相关(四) - 宋马
#address-cells#size-cells 应使用与根节点相同的值,并且 ranges 应为空,以便地址转换逻辑能够正确工作。

3.5.2 /reserved-memory/ child节点

reserved-memory 节点的每个子节点指定一个或多个保留内存区域。每个子节点可以使用 reg 属性来指定一个特定的保留内存范围,或者使用带有可选约束的 size 属性来请求一个动态分配的内存块。

遵循通用名称的推荐实践,节点名称应反映节点的用途(例如,“framebuffer”“dma-pool”)。如果节点是静态分配的,则应在名称后附加单元地址(@<address>)。

保留内存节点需要以下属性之一:

对于静态分配,需要 reg 属性。
对于动态分配,需要 size 属性。

动态分配可以使用 alignmentalloc-ranges 属性来限制内存分配的位置。如果同时存在 regsize 属性,则该区域被视为静态分配,reg 属性优先,size 属性被忽略。

图片[19] - 嵌入式系统内核镜像相关(四) - 宋马
图片[20] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。

no-mapreusable 属性是互斥的,不能在同一个节点中同时使用。

Linux 实现说明:

如果存在 linux,cma-default 属性,则 Linux 会将该区域用作连续内存分配器的默认池。
如果存在 linux,dma-default 属性,则 Linux 会将该区域用作一致性 DMA 分配器的默认池。

3.5.3 对reserved memory的设备节点引用

可以通过在设备节点中添加 memory-region 属性来引用 /reserved-memory 节点中的区域。

3.5.4 /reserved memory和UEFI

当通过UEFI引导时,静态 /reserved-memory 区域也必须列在通过UEFI第 7.2 节中定义的 GetMemoryMap() UEFI 引导时服务获取的系统内存映射中。需要将保留内存区域包含在UEFI内存映射中,以防止UEFI应用程序进行分配。

具有 no-map 属性的保留区域必须以 EfiReservedMemoryType 类型列在内存映射中。所有其他保留区域必须以 EfiBootServicesData 类型列在内存映射中。

动态保留内存区域不应列在UEFI内存映射中,因为它们是在退出固件引导服务后由操作系统分配的。

3.5.5 /reserved memory例子

这个示例为Linux内核定义了3个连续的内存区域:一个默认用于所有设备驱动程序(命名为 linux,cma,大小为 64MiB),一个专门用于帧缓冲设备(命名为 framebuffer@78000000,大小为 8MiB),以及一个用于多媒体处理(命名为 multimedia@77000000,大小为 64MiB)。
图片[21] - 嵌入式系统内核镜像相关(四) - 宋马

3.6 /chosen节点

/chosen 节点并不代表系统中的一个真实设备,而是描述了系统固件在运行时选择或指定的参数。它应当是根节点的子节点。

图片[22] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。

示例:

chosen {
    bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};

某些旧版本的设备树可能包含一个已废弃的属性形式,名为 linux,stdout-path。为了兼容性,如果不存在 stdout-path 属性,客户端程序可能需要支持 linux,stdout-path。这两个属性的含义和用途是相同的。

3.7 /cpus节点属性

所有设备树都需要一个 /cpus 节点。它并不代表系统中的一个真实设备,而是作为子 CPU 节点的容器,这些子节点代表系统的 CPU。

图片[23] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。

/cpus 节点可以包含适用于所有 CPU 节点的通用属性。详细信息请参见第 3.8 节。

示例请参见第 3.8.4 节。

3.8 /cpus/cpu*节点属性

一个 CPU 节点代表一个硬件执行单元,它足够独立,能够运行操作系统而不会干扰其他可能运行其他操作系统的 CPU。

通常,共享一个 MMU(内存管理单元)的硬件线程会被表示在一个 CPU 节点下。如果设计了更复杂的 CPU 拓扑结构,CPU 的绑定必须描述这种拓扑结构(例如,不共享 MMU 的线程)。

CPU 和线程通过一个统一的编号空间进行编号,该编号空间应尽可能与中断控制器对 CPU/线程的编号保持一致。

如果所有 CPU 节点的某些属性值相同,则可以将这些属性放在 /cpus 节点中。客户端程序必须首先检查特定的 CPU 节点,但如果未找到预期的属性,则应查看父 /cpus 节点。这种做法可以减少对所有 CPU 都相同的属性的冗余表示。

每个 CPU 节点的节点名称应为 cpu

3.8.1 /cpus/cpu*节点的通用属性

下表描述了 CPU 节点的一般属性。表 3.9 中描述的某些属性是具有特定适用细节的选择性标准属性。
图片[24] - 嵌入式系统内核镜像相关(四) - 宋马
图片[25] - 嵌入式系统内核镜像相关(四) - 宋马
图片[26] - 嵌入式系统内核镜像相关(四) - 宋马
图片[27] - 嵌入式系统内核镜像相关(四) - 宋马
图片[28] - 嵌入式系统内核镜像相关(四) - 宋马
图片[29] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。
图片[30] - 嵌入式系统内核镜像相关(四) - 宋马
图片[31] - 嵌入式系统内核镜像相关(四) - 宋马
图片[32] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。

某些旧版本的设备树可能在 CPU 节点上包含一个 bus-frequency 属性。为了兼容性,客户端程序可能需要支持 bus-frequency。其值的格式与 clock-frequency 相同。推荐的做法是使用 clock-frequency 属性在总线节点上表示总线的频率。

3.8.2 TLB属性

CPU 节点的以下属性描述了处理器 MMU 中的TLB。

图片[33] - 嵌入式系统内核镜像相关(四) - 宋马

3.8.3 Internal(L1)Cache属性

CPU 节点的以下属性描述了处理器的内部(L1)缓存。

图片[34] - 嵌入式系统内核镜像相关(四) - 宋马
图片[35] - 嵌入式系统内核镜像相关(四) - 宋马
图片[36] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。

某些旧版本的设备树可能包含一个已废弃的属性形式,名为 l2-cache,它是 next-level-cache 属性的旧称。为了兼容性,如果不存在 next-level-cache 属性,客户端程序可能需要支持 l2-cache。这两个属性的含义和用途是相同的。

3.8.4 例子

以下是一个带有单个子 CPU 节点的 /cpus 节点的示例:

3.9 多层次和共享Cache节点(/cpus/cpu*/l?-cache)

处理器和系统可能会实现额外的缓存层级。例如,二级(L2)或三级(L3)缓存。这些缓存可能紧密集成在 CPU 中,或者可能被多个 CPU 共享。

一个具有 compatible 值为 "cache" 的设备节点用于描述这些类型的缓存。

缓存节点应当定义一个 phandle 属性,所有与该缓存相关联或共享该缓存的 CPU 节点或缓存节点都应当包含一个 next-level-cache 属性,该属性指定缓存节点的 phandle

缓存节点可以表示在 CPU 节点下,或者设备树中任何其他合适的位置。

多级缓存和共享缓存通过表 3-9 中的属性来表示。L1 缓存的属性在表 3-8 中描述。

图片[37] - 嵌入式系统内核镜像相关(四) - 宋马
注意:所有其他标准属性(第 2.3 节)都是允许的,但为可选属性。

3.9.1 例子

请参阅以下设备树表示的两个 CPU 的示例,每个 CPU 都有自己的片上 L2 缓存,并且它们共享一个 L3 缓存。

图片[38] - 嵌入式系统内核镜像相关(四) - 宋马
图片[39] - 嵌入式系统内核镜像相关(四) - 宋马

四、设备绑定(Device Bindings)

本章包含了关于特定类型和类别的设备如何在设备树中表示的要求,这些要求称为绑定(bindings)。设备节点的 compatible 属性描述了该节点符合的具体绑定(或多个绑定)。

绑定可以被定义为彼此的扩展。例如,一种新的总线类型可以被定义为 simple-bus 绑定的扩展。在这种情况下,compatible 属性将包含多个字符串,标识每个绑定——从最具体的到最一般的(见第 2.3.1 节,compatible)。

4.1 绑定指南

4.1.1 通用原则

在为设备创建新的设备树表示时,应创建一个绑定,该绑定应完整描述设备所需的属性和值。这组属性应具有足够的描述性,以便为设备驱动程序提供设备所需的属性。

一些推荐的做法包括:

使用第 2.3.1 节中描述的约定定义一个 compatible 字符串。
对于新设备,尽可能使用标准属性(定义于第 2.3 节和第 2.4 节)。这种使用通常至少包括 reginterrupts 属性。
如果新设备符合 DTSpec 定义的设备类别之一,使用第 4 节(设备绑定)中指定的约定。
如适用,使用第 4.1.2 节中指定的杂项属性约定。
如果绑定需要新属性,推荐属性名称格式为:“,”,其中 <company> 是一个 OUI 或类似于股票代码的短唯一字符串,用于标识绑定的创建者。
示例:"ibm,ppc-interrupt-server#s"

4.1.2 杂项属性

本节定义了一系列可能适用于多种设备类型和设备类别的有用属性。它们在这里被定义是为了促进名称和使用的标准化。

clock-frequency属性
图片[40] - 嵌入式系统内核镜像相关(四) - 宋马
reg-shift属性
图片[41] - 嵌入式系统内核镜像相关(四) - 宋马
label属性

4.2 串行设备

4.2.1 串行类绑定

串行设备类别包括各种类型的点对点串行线路设备。串行线路设备的示例包括 8250 UART、16550 UART、HDLC 设备和 BISYNC 设备。在大多数情况下,与 RS-232 标准兼容的硬件适合归入串行设备类别。

I2C 和 SPI(串行外设接口)设备不应表示为串行端口设备,因为它们有自己特定的表示方法。

clock-frequency属性
图片[42] - 嵌入式系统内核镜像相关(四) - 宋马
current-speed属性

4.2.2 National Semiconductor 16450/16550 兼容UART要求

与 National Semiconductor 16450/16550 UART(通用异步收发器)兼容的串行设备应在设备树中使用以下属性进行表示。

图片[43] - 嵌入式系统内核镜像相关(四) - 宋马

4.3 网络设备

网络设备是面向数据包的通信设备。这类设备被假定实现了七层 OSI 模型的第二层(数据链路层),并使用媒体访问控制(MAC)地址。网络设备的示例包括以太网(Ethernet)、光纤分布式数据接口(FDDI)、802.11 和令牌环(Token-Ring)。

4.3.1 网络类绑定

address-bits属性
图片[44] - 嵌入式系统内核镜像相关(四) - 宋马
local-mac-address属性
图片[45] - 嵌入式系统内核镜像相关(四) - 宋马
mac-address属性
图片[46] - 嵌入式系统内核镜像相关(四) - 宋马
max-frame-size属性

4.3.2 以太网特定考量

基于 IEEE 802.3 系列局域网(LAN)标准的网络设备(统称为以太网 Ethernet)可以使用以下属性在设备树中表示,此外还包括网络设备类别中指定的属性。本节中列出的属性是对网络设备类别中列出的属性的补充。

max-speed属性
图片[47] - 嵌入式系统内核镜像相关(四) - 宋马
phy-connection-type属性
图片[48] - 嵌入式系统内核镜像相关(四) - 宋马
终于发现rgmii了!

phy-handle属性

4.4 Power ISA 开放可编程中断控制器(PIC)

本节指定了表示与 Open PIC 兼容的中断控制器的要求。Open PIC 中断控制器实现了由 AMD 和 Cyrix 联合开发的 Open PIC 架构,并在《The Open
Programmable Interrupt Controller (PIC) Register Interface Specification Revision 1.2》中进行了规定。

在 Open PIC 中断域中的中断说明符由两个单元格编码。第一个单元格定义中断号。第二个单元格定义电平和触发方式信息。

中断说明符中的电平和触发方式信息应按以下方式编码:

图片[49] - 嵌入式系统内核镜像相关(四) - 宋马

4.5 simple-bus(简单总线)兼容值

片上系统(System-on-a-chip)处理器可能有一个内部 I/O 总线,该总线无法被探测设备。总线上的设备可以直接访问,无需额外配置。这种类型的总线被表示为一个具有 compatible 值为 “simple-bus” 的节点。

五、扁平化设备树格式(DTB)

设备树二进制大对象(Devicetree Blob,简称 DTB)格式是设备树数据的平面二进制编码。它用于在软件程序之间交换设备树数据。例如,在引导操作系统时,固件会将 DTB 传递给操作系统内核。

注意:IEEE1275 开放固件 [IEEE1275] 并没有定义 DTB 格式。在大多数符合开放固件标准的平台上,设备树是通过调用固件方法遍历树结构来提取的。

DTB 格式在一个单一的、线性的、无指针的数据结构中编码设备树数据。它由一个小的头部(见第 5.2 节),后跟三个可变大小的部分组成:内存保留块(见第 5.3 节)、结构块(见第 5.4 节)和字符串块(见第 5.5 节)。这些部分应该按照该顺序出现在扁平化的设备树中。因此,当整个设备树结构加载到内存地址时,将类似于图 5.1 中的图示(较低的地址位于图示的顶部)。

图片[50] - 嵌入式系统内核镜像相关(四) - 宋马
(空闲空间)部分可能不存在,尽管在某些情况下,可能需要它们来满足各个块的对齐约束(见第 5.6 节)。

5.1 版本管理

自从最初定义该格式以来,已经定义了多个版本的扁平化设备树结构。头部中的字段提供了版本信息,以便客户端程序可以确定设备树是否以兼容的格式编码。
本文档仅描述格式的第 17 版。符合 DTSpec 的引导程序应提供第 17 版或更高版本的设备树,并应提供与第 16 版向后兼容的设备树版本。符合 DTSpec 的客户端程序应接受与第 17 版向后兼容的任何版本的设备树,并可能接受其他版本。

注意:版本是关于设备树的二进制结构,而不是其内容。

5.2 头部

设备树的头部布局由以下 C 结构定义。所有头部字段都是 32 位整数,以大端格式存储。

扁平化设备树头部字段

struct fdt_header {
            
    uint32_t magic;
    uint32_t totalsize;
    uint32_t off_dt_struct;
    uint32_t off_dt_strings;
    uint32_t off_mem_rsvmap;
    uint32_t version;
    uint32_t last_comp_version;
    uint32_t boot_cpuid_phys;
    uint32_t size_dt_strings;
    uint32_t size_dt_struct;
};

magic
该字段应包含值 0xd00dfeed(大端格式)。

totalsize
该字段应包含设备树数据结构的总大小(以字节为单位)。此大小应包括结构的所有部分:头部、内存保留块、结构块和字符串块,以及块之间或最终块之后的任何空闲空间间隙。

off_dt_struct
该字段应包含结构块(见第 5.4 节)相对于头部开始的偏移量(以字节为单位)。

off_dt_strings
该字段应包含字符串块(见第 5.5 节)相对于头部开始的偏移量(以字节为单位)。

off_mem_rsvmap
该字段应包含内存保留块(见第 5.3 节)相对于头部开始的偏移量(以字节为单位)。

version
该字段应包含设备树数据结构的版本。如果使用本文档中定义的结构,则版本为 17。符合 DTSpec 的引导程序可能提供更高版本的设备树,在这种情况下,该字段应包含在提供该版本详细信息的后续文档中定义的版本号。

last_comp_version
该字段应包含与所用版本向后兼容的设备树数据结构的最低版本。因此,对于本文档中定义的结构(版本 17),该字段应包含 16,因为版本 17 与版本 16 向后兼容,但不与更早的版本兼容。根据第 5.1 节,符合 DTSpec 的引导程序应提供与版本 16 向后兼容的设备树格式,因此该字段应始终包含 16。

boot_cpuid_phys
该字段应包含系统引导 CPU 的物理 ID。它应与设备树中该 CPU 节点的 reg 属性中给出的物理 ID 相同。

size_dt_strings
该字段应包含设备树二进制大对象中字符串块部分的长度(以字节为单位)。

size_dt_struct
该字段应包含设备树二进制大对象中结构块部分的长度(以字节为单位)。

5.3 Memory Reservation Block

5.3.1 目的

内存保留块(memory reservation block)为客户端程序提供了一个物理内存中保留区域的列表,即这些区域不应被用于常规内存分配。它用于保护重要的数据结构不被客户端程序覆盖。例如,在一些具有 IOMMU 的系统上,由 DTSpec 引导程序初始化的 TCE(翻译控制条目)表就需要以这种方式进行保护。同样地,任何在客户端程序运行期间使用的引导程序代码或数据也需要被保留(例如,在开放固件平台上的 RTAS)。DTSpec 不要求引导程序提供任何这样的运行时组件,但它也不禁止实现作为扩展来这样做。

更具体地说,除非引导程序提供的其他信息明确指示客户端程序应这样做,否则客户端程序不得访问保留区域内的内存。然后,客户端程序可以按照指示的方式访问指示的保留内存部分。引导程序可以如何向客户端程序指示保留内存的特定用途,可能会出现在本文档中、作为可选扩展,或在特定平台的文档中。

引导程序提供的保留区域可以,但不要求,包含设备树二进制大对象(devicetree blob)本身。客户端程序应确保在它被使用之前不会覆盖这个数据结构,无论它是否在保留区域内。

任何在内存节点中声明并被引导程序访问或由引导程序在客户端进入后导致访问的内存都必须被保留。这种访问类型的例子包括(例如,通过非保护虚拟页面进行的推测性内存读取)。

这一要求是必要的,因为任何未被保留的内存都可能被客户端程序以任意存储属性访问。

引导程序进行的或由引导程序导致的对保留内存的任何访问都必须以非缓存禁止和内存一致性要求(即,WIMG = 0bx01x)的方式进行,并且对于 Book III-S 实现,还必须是非写通要求(即,WIMG = 0b001x)。此外,如果支持 VLE 存储属性,则所有对保留内存的访问都必须以 VLE=0 进行。

这一要求是必要的,因为客户端程序被允许以非写通要求、非缓存禁止和内存一致性要求(即,WIMG = 0b001x)以及在支持的地方 VLE=0 的存储属性映射内存。客户端程序可能使用包含保留内存的大虚拟页面。然而,客户端程序不得修改保留内存,因此引导程序可以在该存储属性的值在架构上允许的情况下,以写通要求的方式对保留内存进行访问。

5.3.2 格式

内存保留块由一对对的64位大端整数列表组成,每对整数由以下C结构表示。

struct fdt_reserve_entry {
            
    uint64_t address;
    uint64_t size;
};

每对整数提供保留内存区域的物理地址和大小(以字节为单位)。这些给定的区域之间不应重叠。保留块列表应以一个地址和大小都等于0的条目结束。注意,地址和大小值始终是64位的。在32位CPU上,值的高32位被忽略。

内存保留块中的每个 uint64_t,因此整个内存保留块,应位于从设备树二进制大对象(devicetree blob)开始的8字节对齐的偏移位置(见第5.6节)。

5.3.3 Memory Reservation Block和UEFI

/reserved-memory 节点(第 3.5.4 节)类似,通过UEFI引导时,内存保留块(Memory Reservation Block)中的条目也必须列在通过 GetMemoryMap() 获取的系统内存映射中,以防止UEFI应用程序进行分配。内存保留块条目应以 EfiReservedMemoryType 类型列出。

5.4 Structure Block

结构块(structure block)描述了设备树本身的结构和内容。它由一系列带有数据的标记(tokens)组成,如下所述。这些标记被组织成一个线性树结构,如下所述。

结构块中的每个标记,因此结构块本身,应位于从设备树二进制大对象(devicetree blob)开始的4字节对齐的偏移位置(见第 5.6 节)。

5.4.1 词法结构

结构块由一系列片段组成,每个片段都以一个标记(token)开始,即一个大端32位整数。一些标记后面跟着额外的数据,其格式由标记值决定。所有标记都应位于32位边界上,这可能需要在前一个标记的数据后插入填充字节(值为0x0)。

五种标记类型如下:

FDT_BEGIN_NODE (0x00000001)
FDT_BEGIN_NODE 标记表示一个节点表示的开始。它后面跟着节点的单元名称作为额外数据。名称存储为以空字符结尾的字符串,并应包括单元地址(如果有的话,见第 2.2.1 节)。节点名称后跟零填充字节(如果对齐需要),然后是下一个标记,该标记可以是除 FDT_END 之外的任何标记。

FDT_END_NODE (0x00000002)
FDT_END_NODE 标记表示一个节点表示的结束。此标记没有额外数据;因此它后面紧跟着下一个标记,该标记可以是除 FDT_PROP 之外的任何标记。

FDT_PROP (0x00000003)
FDT_PROP 标记表示设备树中一个属性表示的开始。它后面跟着描述属性的额外数据。这些数据首先包括属性的长度和名称,表示为以下 C 结构:

struct {
              
    uint32_t len;
    uint32_t nameoff;
}

该结构中的两个字段都是32位大端整数。

len 给出属性值的长度(以字节为单位,可以为零,表示空属性,见第 2.2.4 节)。
nameoff 给出字符串块中的偏移量(见第 5.5 节),其中存储属性名称作为以空字符结尾的字符串。
在此结构之后,属性的值作为长度为 len 的字节字符串给出。此值后面跟着零填充字节(如果需要),以对齐到下一个32位边界,然后是下一个标记,该标记可以是除 FDT_END 之外的任何标记。

FDT_NOP (0x00000004)
解析设备树的任何程序都将忽略 FDT_NOP 标记。此标记没有额外数据;因此它后面紧跟着下一个标记,该标记可以是任何有效标记。树中的属性或节点定义可以用 FDT_NOP 标记覆盖,从而无需移动设备树二进制大对象中树表示的其他部分即可将其从树中移除。

FDT_END (0x00000009)
FDT_END 标记表示结构块的结束。只能有一个 FDT_END 标记,并且它应该是结构块中的最后一个标记。它没有额外数据;因此 FDT_END 标记之后的字节相对于结构块开始的偏移量等于设备树二进制大对象头部中 size_dt_struct 字段的值。

5.4.2 树结构

设备树结构被表示为一个线性树:每个节点的表示都以 FDT_BEGIN_NODE 标记开始,并以 FDT_END_NODE 标记结束。节点的属性和子节点(如果有的话)在 FDT_END_NODE 之前表示,这样那些子节点的 FDT_BEGIN_NODEFDT_END_NODE 标记就嵌套在父节点的标记之内。

整个结构块由根节点的表示组成(它包含所有其他节点的表示),后面跟着一个 FDT_END 标记,以标记整个结构块的结束。

更准确地说,每个节点的表示包括以下组成部分:

(可选)任意数量的 FDT_NOP 标记
FDT_BEGIN_NODE 标记

节点名称作为一个以空字符结尾的字符串
[对齐到4字节边界的零填充字节]

对于节点的每个属性:

(可选)任意数量的 FDT_NOP 标记
FDT_PROP 标记

属性信息如第 5.4.1 节所述
[对齐到4字节边界的零填充字节]

所有子节点的表示以这种格式
(可选)任意数量的 FDT_NOP 标记
FDT_END_NODE 标记

请注意,这一过程要求特定节点的所有属性定义都位于该节点的任何子节点定义之前。尽管如果属性和子节点混合在一起,结构也不会产生歧义,但通过这一要求简化了处理扁平树所需的代码。

5.5 Strings Block

字符串块(strings block)包含了表示树中使用的所有属性名称的字符串。这些以空字符结尾的字符串简单地在此部分中串联在一起,并通过字符串块中的偏移量从结构块中引用。

字符串块没有对齐约束,并且可以出现在设备树二进制大对象(devicetree blob)开始的任何偏移处。

5.6 对齐

设备树二进制大对象(devicetree blob)应位于8字节对齐的地址。为了保持与32位机器的向后兼容性,一些软件支持4字节对齐,但这不符合 DTSpec 规范。

为了让内存保留块和结构块中的数据在不进行未对齐内存访问的情况下被使用,它们应位于适当对齐的内存地址。具体来说,内存保留块应与8字节边界对齐,结构块应与4字节边界对齐。

此外,整个设备树二进制大对象可以被重新定位,而不破坏子块的对齐。如前几节所述,结构块和字符串块应从设备树二进制大对象的开始处有对齐的偏移量。为了确保块在内存中的对齐,只需确保整个设备树加载在一个对齐到任何子块最大对齐要求的地址上,即8字节边界。符合 DTSpec 规范的引导程序应在将其传递给客户端程序之前,将设备树二进制大对象加载到这样一个对齐的地址上。如果符合 DTSpec 规范的客户端程序在内存中重新定位设备树二进制大对象,它应该只将其重新定位到另一个8字节对齐的地址上。

六、设备树源格式(DTS)

设备树源文件(Devicetree Source,简称 DTS)格式是一种文本形式的设备树表示,可以被 dtc 处理成内核预期的二进制设备树形式。以下描述不是 DTS 的正式语法定义,而是描述了用于表示设备树的基本构造。

DTS 文件的名称应以“.dts”结尾。

6.1 编译器指令

DTS 文件中可以包含其他源文件。包含文件的名称应以“.dtsi”结尾。被包含的文件反过来又可以包含其他文件。

/include/ "文件名"

6.2 标签

源格式允许在设备树中的任何节点或属性值上附加标签(labels)。通过引用标签而不是显式指定 phandle 值或节点的完整路径,可以自动生成 phandle 和路径引用。标签仅用于设备树源格式中,并且不会编码到 DTB 二进制文件中。

标签的长度应在 1 到 31 个字符之间,只能由表 6.1 中的字符集组成,并且不能以数字开头。

通过在标签名称后附加一个冒号(:)来创建标签。通过在标签名称前加上一个和号(&)来创建引用。

6.3 节点和属性定义

设备树节点使用节点名称和单元地址定义,大括号标记节点定义的开始和结束。它们可以由标签前导。

[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};

节点可以包含属性定义和/或子节点定义。如果两者都存在,属性应在子节点之前。

先前定义的节点可以被删除。

/delete-node/ node-name;
/delete-node/ &label;

属性定义是名称值对,形式如下:

[label:] property-name = value;

除了属性值为空(零长度)的情况,其形式为:

[label:] property-name;

先前定义的属性可以被删除。

/delete-property/ property-name;

属性值可以定义为32位整数单元数组、以空字符结尾的字符串、字节字符串或这些的组合。

单元数组由尖括号包围,包含由空格分隔的C风格整数列表。示例:

interrupts = <17 0xc>;

值可以表示为括号内的算术、位或逻辑表达式。
算术运算符

+
-
*
/
%
位运算符
&
|
^ 异或
~
<< 左移
>> 右移
逻辑运算符
&&
||
!
关系运算符
< 小于
> 大于
<= 小于或等于
>= 大于或等于
== 等于
!= 不等于
三元运算符
?: (条件?为真时的值:为假时的值)

64位值由两个32位单元表示。示例:

clock-frequency = <0x00000001 0x00000000>;

以空字符结尾的字符串值使用双引号表示(属性值被认为包括终止的NULL字符)。示例:

compatible = "simple-bus";

字节字符串被方括号 [ ] 包围,每个字节由两个十六进制数字表示。字节之间的空格是可选的。示例:

local-mac-address = [00 00 12 34 56 78];

或等价地:

local-mac-address = [000012345678];

值可以有多个由逗号分隔的组件,这些组件被串联在一起。示例:

compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";

在单元数组中,对另一个节点的引用将被展开为该节点的 phandle。引用可以是 & 后跟节点的标签。示例:

interrupt-parent = < &mpic >;

或者它们可以是 & 后跟节点的完整路径在大括号中。示例:

interrupt-parent = < &{/soc/interrupt-controller@40000} >;

在单元数组之外,对另一个节点的引用将被展开为该节点的完整路径。示例:

ethernet0 = &EMAC0;

标签也可以出现在属性值的任何组件之前或之后,或单元数组的单元之间,或字节字符串的字节之间。示例:

reg = reglabel: <0 sizelabel: 0x1000000>;
prop = [ab cd ef byte4: 00 ff fe];
str = start: "string value" end: ;

6.4 文件布局

版本 1 的 DTS 文件具有以下整体布局:

/dts-v1/;
[memory reservations]
/ {
    [property definitions]
    [child nodes]
};

/dts-v1/; 必须存在,以标识文件为版本 1 的 DTS(没有此标签的 dts 文件将被 dtc 视为过时的版本 0,该版本除了其他小的不兼容更改外,还使用了不同的整数格式)。

内存保留(见第 5.3 节)由以下形式的行表示:

/memreserve/ <address> <length>;

其中 <address><length> 是 64 位 C 风格整数,例如:

/* 保留内存区域 0x10000000..0x10003fff */
/memreserve/ 0x10000000 0x4000;

/ { ... }; 部分定义了设备树的根节点,所有设备树数据都包含在其中。

支持 C 风格(/* ... */)和 C++ 风格(//)的注释。


总结

翻译了《Devicetree Specification Release v0.4》

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容