| RFC-0192:Fuchsia 上的设备树 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 一种使用设备树描述硬件布局的策略 |
| 问题 | |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2022-08-03 |
| 审核日期(年-月-日) | 2022-10-01 |
摘要
扁平化设备树(简称“FDT”或“设备树”)是一种广泛用于描述主板上硬件布局的格式。它们与 ACPI 的用途大致相似,但它们在更低的抽象级别上运行,从而将更多复杂性推送到操作系统中。FDT 规范定义了二进制设备树 Blob (“DTB”) 格式和源格式 (“DTS”),设备树 Blob 就是从源格式编译而来的。
此 RFC 提出了一种实用的方法,可在 Fuchsia 中引入对设备树的支持,而无需将设备树(无论是其布局还是二进制格式)作为 ABI 来提交。相反,设备树的确切 ABI 由主板驱动程序在自身与固件之间定义。另请注意,此 RFC 仅涉及板级驱动程序对设备树的使用。如果内核解析设备树,或者当内核解析设备树时,将有单独的 RFC 描述相应设备树的格式。
设计初衷
仅靠主板驱动程序无法持续扩大 Fuchsia 的硬件支持范围,原因如下:
- 我们希望能够迭代硬件配置,而无需重新组装/重新构建 Fuchsia 映像。
- 开发者广泛使用设备树,我们希望满足开发者的需求。设备树还具有相对完善的生态系统。
- 目前,这些标准不适合多个组织之间的协作。
- 它们会导致固件和主板驱动程序之间出现重复工作,以检测系统中的可用硬件。
- 在支持多个相关主板(即共享 SoC 或 SoC 系列)时,它们无法很好地进行扩展。
- 为了支持多个主板,必须在整个主板驱动程序中添加条件,或者拥有多个主板驱动程序。
- 虽然可以采用简洁的方式实现此目的,但与设备树相比,更容易生成意大利面式代码。
- 借助设备树,我们可以更轻松地使用单个 Fuchsia 映像支持多个主板(不过请注意,我们目前的目标不是“通用”Fuchsia 映像)。
设备树通过为硬件配置提供单一的事实来源来解决这些问题,该事实来源易于与系统的其余部分分开操作(易于通过启动固件和从事启动工作的群组进行修改),并且还易于组合(允许在基于同一 SoC 系列的板之间轻松共享配置)。
请注意,此 RFC 的目标并非完全消除主板驱动程序。主板驱动程序仍然是 Fuchsia 驱动程序拓扑的核心部分 - 此 RFC 建议使用设备树来补充主板驱动程序,以提高其灵活性。任何无法使用设备树表达的内容仍可在主板驱动程序中实现。
利益相关方
辅导员:
cpu@google.com
审核者:
- surajmalhotra@google.com
- curtisgalloway@google.com
- gkalsi@google.com
- bradenkell@google.com
- cja@google.com
- dpursell@google.com
已咨询:
- aaronwood@google.com
- mcgrathr@google.com
共同化:
此提案的简短概要版本已在内部共享。
术语库
- 板级驱动程序 - 一种向驱动程序框架告知系统上存在的硬件的驱动程序。系统上只有一个主板驱动程序实例。
- 固件 - 系统首次开机时运行的部分,负责加载 Zircon 并启动它。
设计
本文档中的关键字“必须”“不得”“必需”“会”“不会”“应”“不应”“建议”“可以”和“可选”应按照 IETF RFC 2119 中的描述进行解释。
设备树的作用
设备树仅用于描述硬件布局信息。产品配置必须通过产品组装完成。设备树应仅包含与主板相关的真实信息,而无论主板运行的是哪个产品,而不是配置可能因产品而异的驱动程序行为,例如预分配的内存缓冲区大小或分区映射。
设备树与 ACPI
我们更倾向于使用 ACPI 而不是设备树。如果主板支持 ACPI,则应使用 ACPI 启动。在支持 ACPI 的主板上,不得使用设备树。 这是因为 Fuchsia 为每个受支持的架构提供了一个 ACPI 板驱动程序。ACPI 提供的抽象级别更高,标准化程度也更高,因此更容易支持。
兼容性
我们的设备树绑定不会尝试与 Linux 或任何其他操作系统保持兼容性,尤其是对于设备驱动程序绑定。这仅仅是因为,与 ACPI 不同,设备树并未广泛标准化,并且绑定的确切布局和含义因操作系统版本而异。不过,我们将采用规范中所述的源代码和二进制格式。
Fuchsia 平台不会尝试指定设备树的确切格式,也不会尝试使用单个主板驱动程序来支持每个主板;相反,我们会根据规范定义一组绑定,以提供对常见功能(如中断、GPIO、总线)的支持。使用设备树的板级驱动程序应支持此 RFC 中定义的绑定,但它们可以根据需要定义自己的绑定来支持其他功能。
我们还将提供一种方法,让设备树指定其编写时所针对的板驱动程序(例如,通过根节点的属性,如 fuchsia,board-driver = "vim3-devicetree"),板驱动程序应使用该方法来确保它们正在解析兼容的设备树。
此外,我们还将定义一个接口,板级驱动程序必须向下游驱动程序公开该接口。此接口将与 ACPI 接口非常相似,我们将努力使 ACPI 和设备树保持一致,以便驱动程序能够真正不受两者影响。
将设备树传递给 Fuchsia。
我们已有一个类型为 ZBI_TYPE_DEVICETREE 的 ZBI 项。此 ZBI 项的内容是设备树 blob。我们只需在此处更新文档,以反映它可能会被主板驱动程序使用。
主板可以选择使用设备树。在这种情况下,引导加载程序应知道这一点,并且引导加载程序应将设备树作为 ZBI_TYPE_DEVICETREE 项传递。如果需要,主板可以在组装时(例如在启动加载程序内置支持 ZBI 之前,在启动过程中)直接在 ZBI 中包含设备树。
一般来说,引导加载程序应从非易失性存储空间加载设备树,验证其真实性,并在运行时将其附加到 ZBI。不过,如果需要,它们可以使用替代机制(例如将其编译到引导加载程序中),并且可以在适当的情况下(例如对于开发者板)跳过验证。
Fuchsia 平台实现
我们将为希望实现这组绑定的主板驱动程序提供一个库。此库将利用树中现有的设备树解析器。此外,我们还将实现一个参考板驱动程序和设备树,以利用此库启动真实系统。前者将成为 SDK 的一部分并位于树中,但后者最终可能会移出树。
设备树的可读性
设备树的一个缺点是,由于设备树编译器不支持命名常量,因此其源格式可能相当不透明。一种常见的解决方法是使用包含 #define 语句的 C 头文件,这些语句用于为常量声明易于理解的名称,然后包含这些头文件,以便在设备树源中使用它们。我们将在 fuchsia.git 中实现此方法,未来还将支持从绑定库生成此类头文件。
一般来说,支持与设备树搭配使用的驱动程序会有一个目录,其中包含供设备树源文件使用的头文件。这些头文件将定义引用驱动程序公开的资源时使用的值 - GPIO 驱动程序可能具有 PULL_UP、PULL_DOWN 等常量。设备树源文件 build 规则随后将取决于它们使用的驱动程序,这些驱动程序将用于验证设备树(请参阅文档),还会添加到提供给 C 预处理器的 include 路径中。
设备树的可审核性
为了提高设备树的可读性,我们还将引入设备树“黄金”文件。这些黄金文件将是编译设备树源代码为二进制文件,然后再编译回源代码的输出结果,这样一来,源代码更改对最终设备树的影响就会一目了然。编译主板驱动程序和设备树时,我们将编译和反编译设备树源,如果输出与黄金输出不同,我们将使 build 失败。
此功能的应用场景例如是,多个主板包含一个通用的设备树源文件(例如由 SoC 定义的文件)。审核者可能无法立即清楚地了解更改“共享”设备树文件的影响。黄金文件可清晰显示每个设备树在设备上使用时的最终输出。
节点
每个设备树节点都将对应一个 Fuchsia 设备节点。Fuchsia 设备节点将是复合节点,是表示设备树节点的节点和表示设备所消耗资源的节点(例如 I2C 设备、GPIO 等)的子节点。这与 ACPI 使用的方法类似。请注意,以下示例并非详尽无遗,仅旨在说明我们最初将支持的资源类型。未来可以根据需要添加其他资源类型。具体而言,本文档中介绍的 FIDL 接口和绑定规则并非最终版本,而是旨在说明接口将支持哪些功能的初步示例。预计这些接口在未来会接受更全面的审核。
设备元数据节点
设备元数据节点将公开以下 FIDL 协议。此协议旨在用作设备驱动程序可使用的通用“非结构化元数据”协议。
library fuchsia.hardware.metadata;
using zx;
/// Maximum length of a property key.
const PROP_MAX: uint32 = 128;
/// Reasonable maximum for amount of data in a byte array.
const MAX_BYTE_ARRAY_LENGTH: uint32 = 4096;
/// Reasonable maximum length for a string.
const MAX_STRING_LENGTH: uint32 = 4096;
/// Reasonable maximum number of entries for a string array.
const MAX_STRING_ARRAY_LENGTH: uint32 = 4096;
/// Maximum number of properties in a dictionary.
const MAX_PROPERTIES: uint32 = 128;
type Value = flexible union {
/// True if property is present but has no value.
1: present bool;
/// Little-endian 32-bit value.
2: u32 uint32;
/// Little-endian 64-bit value.
3: u64 uint64;
/// String or string array.
4: string vector<string:MAX_STRING_LENGTH>:MAX_STRING_ARRAY_LENGTH;
/// Byte array
5: bytes vector<uint8>:MAX_BYTE_ARRAY_LENGTH;
/// Child node.
6: node Dictionary;
};
type Property = flexible table {
/// Name of the property.
1: name string:PROP_MAX;
/// Value of the property.
2: value Value;
};
type Dictionary = struct {
/// List of properties. There may be duplicate property names, in which case the first one should win.
1: properties vector<Property>:MAX_PROPERTIES;
};
protocol NodeMetadata {
/// Get unstructured metadata belonging to this node.
GetMetadata() -> (Dictionary) error zx.status;
};
绑定
每个部分都将介绍设备树绑定以及如何在 Fuchsia 上使用相应功能。
惯例和标准属性
我们将使用规范(版本 0.3)的第 2.2 节中所述的惯例。
在第 2.3 节中定义的标准属性中,我们可能会支持以下属性:compatible、phandle、status、#address-cells、#size-cells、reg、ranges。我们省略了 virtual-reg,因为引导加载程序跳转到内核时 MMU 的状态与主板驱动程序无关(因为它位于用户空间中)。我们省略了 dma-ranges 和 dma-coherent,因为这些属性在 Fuchsia 上没有直接的等效属性。
在 Fuchsia 上:
- 兼容 - 兼容数组中的第一个值将作为绑定属性
fuchsia.devicetree.first_compatible公开。未来,当更复杂的绑定系统(具有优先级)可用时,可能会使用其他值。- 此外,我们还将允许节点定义自己的绑定属性,这很可能通过命名空间属性来实现(例如,
bind,<prop-name> = <value>将对应于值为value的绑定属性prop-name)。
- 此外,我们还将允许节点定义自己的绑定属性,这很可能通过命名空间属性来实现(例如,
- phandle - 将用于在设备树中唯一标识设备节点。由设备树编译器生成,仅在运行时使用。
- 状态 - 可在主板驱动程序中使用,以控制是否发布节点。在实现此 RFC 期间,我们将调查从受支持的绑定中省略 status 的可行性,如果这样做能显著提高设备树源文件的清晰度,我们可能会选择省略它。
- 具体而言,我们可能希望鼓励将设备树源文件组合在一起(将单个 SoC 定义拆分到多个文件中,并在最终主板文件中仅包含必要的源文件),而不是将它们包含在一个文件中,并通过 status 属性停用/启用 IP 块。
- #address-cells、#size-cells - 将在主板驱动程序中用于确定其他值的大小。
- reg - 对于可从树根寻址的节点,将由主板驱动程序用于确定物理内存区域。这些内存区域将通过
GetMmio(int index)FIDL 调用(可能通过平台总线驱动程序)提供给子节点。 - 范围 - 在主板驱动程序中使用,用于确定子地址范围如何映射到父地址范围。
中断
中断绑定将按照规范的第 2.4 节中的定义进行。
主板驱动程序将向每个中断控制器驱动程序提供以下元数据:
/// Data for an individual interrupt.
type InterruptData = flexible table {
/// The cells associated with an interrupt. The size of this vector
/// is determined by the `#interrupt-cells` property.
1: cells vector<uint32>:MAX;
};
/// Data for an interrupt controller.
type InterruptControllerData = flexible table {
/// This vector contains all of the interrupts discovered by the board driver.
1: configuration vector<InterruptData>:MAX;
};
然后,每个中断驱动程序将为每个中断发布一个节点。节点将具有以下绑定规则:
fuchsia.devicetree.node_type == INTERRUPT;
fuchsia.devicetree.phandle == <phandle of interrupt controller devicetree node>;
fuchsia.devicetree.cellN == <nth configuration cell>;
请注意,phandle 是每个设备树的唯一值,应仅用于由主板驱动程序生成的绑定规则中。
节点将发布以下 FIDL 服务:
library fuchsia.hardware.interrupt;
protocol Provider {
/// Return the interrupt represented by this provider.
Get(struct {}) -> (resource struct { irq zx.interrupt; }) error zx.status;
};
在大多数情况下,中断应直接对应于物理硬件中断,但在某些情况下(例如 GPIO),中断可能会被多路复用,并涉及中间驱动程序。我们未来可能会进行相关工作,以允许从内核内部处理这些中断,但此类工作不在本文档的范围内。
I2C、SPI、UART 和其他外围总线
这些总线上的设备被建模为其控制器设备的子设备。为了确定控制器的总线类型,我们将为每种受支持的总线类型定义一个额外的“兼容”值。例如,i2c 控制器的最后一个兼容值将为 fuchsia,i2c-controller。
我们将定义特定于总线的设备树属性,这些属性用于填充 Fuchsia 总线驱动程序使用的元数据。这些属性可能会与其他操作系统使用的等效属性保持一致,但确切的格式将在 //docs 中记录。
表示 I2C/SPI 等总线的节点将称为 i2c000、spi000 等。请注意,尽管设备树只能表示此类别的单个父节点,但我们仍会为父节点编号,以保持与 ACPI 的兼容性。
GPIO
GPIO 与其他资源的主要区别在于,它们具有一定程度的客户端配置。例如,客户端可能想要启用内部上拉/下拉电阻,或者引脚可能是高电平有效/低电平有效。
GPIO 控制器节点必须具有 #gpio-cells 属性,用于定义用于标识节点公开的 GPIO 的单元格数量。此外,它还必须具有 gpio-controller 属性,该属性为空。
如果 GPIO 控制器节点没有 compatible 字符串,则 GPIO 控制器节点的父节点将改为提供与其子节点相关的元数据。适用于单个逻辑“控制器”公开多个引脚组的 SoC。
我们将定义以下元数据,以便控制器驱动程序了解每个引脚实例的预期配置。请注意,此元数据并非特定于设备树。不过,我们将为设备树定义一些惯例:
- 最后一个配置单元包含
GpioConfiguration标志。 - 其他单元格会放入每个图钉元数据的
data向量中。
元数据类型可能如下所示:
library fuchsia.hardware.gpio;
using fuchsia.driver.framework;
/// Maximum number of properties a pin can have.
const uint32 MAX_PIN_PROPERTIES = 32;
/// Maximum number of configuration cells a driver can have.
const uint32 MAX_PIN_CELLS = 8;
/// GPIO configuration flags.
type GpioConfiguration = flexible bits {
/// If this bit is set, GPIO is active-low. Otherwise, GPIO is active-high.
GPIO_ACTIVE_LOW = 0x1,
// more properties here as appropriate.
};
type GpioPinMetadata = flexible table {
/// Properties the published device should have.
1: expected_properties vector<fuchsia.driver.framework.NodePropertyValue>:MAX_PIN_PROPERTIES;
/// Arbitrary, driver-specific data. This will likely encode the pin number.
2: data vector<uint32>:MAX_PIN_CELLS;
/// GPIO configuration flags.
3: flags GpioConfiguration;
};
type GpioMetadata = flexible table {
/// Standard metadata for GPIO pins.
/// The GPIO controller driver should publish a GPIO pin node for each of these.
1: pins vector<GpioPinMetadata>:MAX;
};
此外,GPIO 控制器通常是较大“引脚控制器”的一部分,其中一组 GPIO 控制器由单个驱动程序管理。为了支持这一点,如果驱动程序不会绑定到单个 GPIO 控制器节点(即,未在其上设置兼容或其他绑定属性),我们将向父节点提供此元数据。
每个已发布的图钉都应具有以下属性:
fuchsia.devicetree.node_type == GPIO;
fuchsia.devicetree.phandle == <phandle>;
fuchsia.devicetree.cellN == <nth configuration cell>;
想要使用 GPIO 的设备树节点应使用名为 <name>-gpios 的属性。这些将最终成为名为 gpio-<name>NNN 的 fragment 父项。如果命名了多个 gpios,系统会根据它们的索引为其分配编号。
这些节点应公开 fuchsia.hardware.gpio.Gpio 协议。
如果驱动程序要绑定到 GPIO,则需要如下绑定规则:
node "gpio" {
fuchsia.resource.name == "example";
fuchsia.resource.index == 0; // zeroth gpio in the "example-gpios" list.
fuchsia.hardware.gpio.Gpio == ZirconTransport;
}
稳压器
我们将以与 GPIO 类似的方式处理电压调节器,但不会向调节器驱动程序提供任何元数据。监管节点将根据其在标记为 -supply 的媒体资源中的使用情况进行推断。
单个调节器应对应于单个设备树节点,因此无需额外配置。
调节器驱动程序应发布具有以下属性的节点:
fuchsia.devicetree.node_type == REGULATOR;
fuchsia.devicetree.phandle == <phandle>;
一个供应节点只能引用一个调节器。这些 fragment 应公开 fuchsia.hardware.vreg.Vreg 协议。
监管机构消费者随后将使用绑定规则,例如:
node "regulator" {
fuchsia.resource.name == "vdd"; // equivalent to vdd-supply in devicetree.
fuchsia.hardware.vreg.Vreg == ZirconTransport;
}
时钟和其他资源
这些设备可以分配有多个实例。
时钟与电压调节器非常相似。由于时钟驱动程序应知道其导出的时钟数量,因此无需元数据。时钟控制器节点应定义 #clock-cells,即识别时钟设备所需的单元数。
它们应具有以下属性:
fuchsia.devicetree.node_type == CLOCK;
fuchsia.devicetree.phandle == <phandle>;
fuchsia.devicetree.cellN == <nth configuration cell>;
时钟消费者可以通过在 clocks 属性中指定时钟标识符数组,并在 clock-names 属性中指定可选名称数组来定义时钟。
使用时钟的设备将具有名为 clk-<name>(如果存在 clock-names)或 clk-NNN 的 fragment 父项,其中 NNN 是 clocks 数组中时钟的索引。每个 fragment 都将公开 fuchsia.hardware.clock.Device 协议。
如需绑定到时钟节点,驱动程序会使用如下绑定规则:
// If clock-names is expected:
node "clock-input" {
fuchsia.resource.name == "input";
fuchsia.hardware.clock.Device == ZirconTransport;
}
// If clock-names is not expected:
node "clock-input" {
fuchsia.resource.index == 0;
fuchsia.hardware.clock.Device == ZirconTransport;
}
端到端示例
以 vim3 USB PHY 为例。严格来说,Fuchsia 驱动程序控制着两个 USB PHY 和一个特殊的复用器,该复用器可在主机模式和外围设备模式之间路由其中一个 PHY。
如果我们特别关注此等式中的“mux”部分,则它具有以下资源:
- 一个 MMIO 区域。
- 一次中断。
- 一个时钟。
其设备树节点如下所示:
/ { // Root node of the devicetree.
// 64 bit addresses.
#address-cells = <2>;
#size-cells = <2>;
#interrupt-parent = <&gic>;
usb-mux@ffe09000 {
compatible = "amlogic,g12b-usb-mux";
reg = <0x0 0xffe09000 0x0 0xa0>; // MMIO region.
interrupts = <GIC_SPI 16 INTERRUPT_MODE_EDGE_HIGH>; // Interrupt.
clocks = <&clk CLK_G12B_USB>; // Clocks.
clock-names = "usb"; // Clock names.
};
gic: interrupt-controller@ff000000 {
compatible = "arm,gic-400";
interrupt-controller; // This is an interrupt controller.
#interrupt-cells = <3>; // 3 32-bit values are used to identify interrupt on this controller.
};
};
根据此节点布局(专门查看 usb-mux 节点),设备树板驱动程序将执行以下操作:
- 查看
reg节点,并将 MMIO 区域从0xffe09000...0xffe090a0添加到平台设备的定义中。 - 查看中断资源,并向设备组添加一个中断节点,该节点具有中断部分中所述的属性。
- 查看时钟资源,并向设备组添加一个具有时钟部分中所述属性的时钟节点。
如需绑定到此触屏设备,设备驱动程序的复合绑定文件应如下所示:
composite g12b_usb_mux;
primary node "pdev" {
fuchsia.devicetree.first_compatible == "amlogic,g12b-usb-mux";
fuchsia.hardware.platform.device.PDev == ZirconTransport;
}
node "clock-usb" {
fuchsia.hardware.clock.Device == ZirconTransport;
fuchsia.resource.name == "usb";
}
node "interrupt" {
fuchsia.hardware.interrupt.Provider == ZirconTransport;
fuchsia.resource.index == 0;
}
请注意,驱动程序看到的绑定属性与用于匹配复合属性的绑定属性不同,因为我们将利用设备组提供的转换 API。
实现
我们可能会通过多个 CL 来引入实现此 RFC 的代码。 由于在此阶段不会向树外 SDK 添加任何 API,因此实现过程本身不太可能特别复杂。
这些 API 最终将成为驱动程序 SDK 的一部分,但我们首先计划在树内验证设计,以便更轻松地快速迭代。
性能
此 RFC 不太可能增加任何显著的运行时性能开销,因为所提议的大部分逻辑都在启动时执行一次。
安全注意事项
设备树是由系统固件提供或包含在 ZBI 中的二进制 Blob,本身就是系统受信任的一部分。此外,驱动程序只能访问属于其节点的资源。虽然它们可以检查子节点的名称和属性,但由于系统本身的性质,它们无法访问子节点拥有的任何资源 - 资源访问权限由主板驱动程序通过复合节点的 fragment 父节点明确授予。
不过,驱动程序将能够解析设备树中的属性,如果编写不当,可能会导致驱动程序容易受到攻击。具体来说,我们将为驱动程序提供定义其希望包含在设备树中的配置数据架构的功能,但此解析代码可能容易受到攻击,因此我们应确保可以轻松测试(或模糊测试)使用此类配置数据的驱动程序。
由于主板驱动程序不属于 Fuchsia 平台,因此对设备树库或主板驱动程序的任何安全修复都需要由相应主板驱动程序的所有者纳入,并且需要重新构建新的主板驱动程序。
隐私注意事项
对设备树的访问权限将受到限制,因为它可以用于对设备进行指纹识别。
测试
最初,我们将使用单元测试来验证主板驱动程序的各个功能。最终,我们将使用任意设备树进行集成测试,以创建设备拓扑。
我们还将对设备树解析器进行模糊测试。
我们还将提供工具来根据已知的设备树架构验证设备树。
文档
我们将在 fuchsia.dev 上的一份文档中记录 Fuchsia 对设备树的使用以及我们支持设备树的方法。具体来说,我们希望主板所有者了解如何在他们尝试启动的主板上使用设备树,了解我们的方法与其他操作系统有何不同,以及如何实际让设备树发挥作用。
此外,我们还将要求驱动程序指定它们预期使用的设备树架构,并将其包含在 build 中。为确保文档符合预期并保证设备树的正确性,我们将实现架构验证,以便根据这些架构验证生产设备树。
缺点、替代方案和未知因素
缺点:可读性
设备树相对而言难以阅读。特别是,将不同的源文件组合在一起以生成最终输出的模式意味着,您需要解析所有源文件才能了解最终结果。
我们希望通过工具(即黄金文件)来解决这个问题,但使用设备树时,这可能会成为一个摩擦点。
替代方案:继续使用纯主板驱动程序
我们可以继续在主板驱动程序中以编程方式表达所有硬件配置,但这需要编写代码并重新构建驱动程序才能表达更改。如上所述,主板驱动程序并不是一种可持续的方法,无法持续扩展 Fuchsia 的主板生态系统。
替代方案:使用其他机制来描述硬件
我们可以使用另一种基于数据的机制来描述硬件布局。与假设的替代方案相比,设备树的主要优势包括:
- 广泛使用,硬件生态系统中的参与者非常熟悉。
- 通用构建块(例如中断支持)定义明确且易于采用。
- 我们可以利用现有工具。
设备树的主要缺点是:
- 没有明确的跨操作系统标准化工作。大多数活动都围绕 Linux 展开。
- 许多现有绑定仅针对 Linux 驱动程序编写。
- 除了硬件配置之外,还很容易被滥用于其他方面(例如,产品专用配置)。
- 当多个设备树叠加并相互更改对方的节点(例如
status = "disabled"/status = "okay"可以停用/启用节点)时的复杂性。
此外,还没有出现明显的替代设备树的方案(大多数操作系统要么使用设备树,要么使用主板文件来解决此问题)。设备树自 1994 年以来就已存在,并且已牢固确立为描述硬件的主要方式。
替代方案:定义由 Fuchsia 平台强制执行的稳定设备树架构
我们可以定义一个由单个通用“devicetree”板驱动程序支持的 devicetree 架构,并将其作为使用 devicetree 的官方方式。
这将使我们更接近于为所有使用设备树的系统提供单个 Fuchsia 映像的目标,但设备树的差异很大(与 ACPI 等相比),这使得这项工作非常艰巨,并且可能需要大量改进。
此 RFC 并未排除未来采用此类架构的可能性,但我们认为,目前采用设备树最务实的方法是上述方法。
替代方案:通过更广泛的生态系统稳定设备树绑定
我们可以与其他使用设备树的群组合作,在更广泛的生态系统中实现绑定标准化。
这可能需要多年的努力,不在本 RFC 的讨论范围内。Fuchsia 不太可能能够主导这项工作,原因很简单,绝大多数设备树都不是为 Fuchsia 编写的。
替代方案:自行开发 DSL
我们可以推出自己的 DSL,与 FIDL 和绑定规则集成,而不是使用设备树。这可能是我们将来想要实现的功能,但在撰写本文时,我们还没有关于此 DSL 的具体实现想法。如果我们将来确实认为需要 DSL,那么实现此 RFC 的经验教训很可能会对 DSL 的设计产生宝贵的影响。
在先技术和参考资料
- Linux 和设备树 - 简要介绍 Linux 如何使用设备树。
- ARM 的设备树 - 这是 2009 年的一场讲座,当时 Linux 正在考虑为 ARM 采用设备树。
- Devicetree:过去、现在、未来 - 2019 年关于 Linux 上设备树状态的讲座。
- devicetree.org - devicetree 规范组织。
- ACPI 与 DT - 关于统一 Linux 中的 ACPI 和设备树接口的讲座。