RFC-0192:Fuchsia 上的设备树 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 使用设备树描述硬件布局的策略 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2022-08-03 |
审核日期(年-月-日) | 2022-10-01 |
摘要
扁平化设备树(“FDT”或简称“devicetree”)是一种广泛用于描述开发板上硬件布局的格式。它们的用途与 ACPI 大致相似,但它们在抽象层级上要低得多,会将更多复杂性推送到操作系统。FDT 规范定义了二进制设备树 blob (“DTB”) 格式,以及用于编译设备树 blob 的源格式 (“DTS”)。
此 RFC 提出了一种务实的方法,可在 Fuchsia 中引入对设备树的支持,而无需将设备树(其布局或二进制格式)作为 ABI。而是将设备树的确切 ABI 留给板级驱动程序在其自身与固件之间进行定义。另请注意,此 RFC 仅涉及板级驱动程序对设备树的使用。如果或当内核解析设备树时,单独的 RFC 将描述所述设备树的格式。
设计初衷
仅使用开发板驱动程序并不能持续扩大 Fuchsia 的硬件支持,原因有很多:
- 我们希望能够在不重新组装/重新构建 Fuchsia 映像的情况下迭代硬件配置。
- Devicetree 被开发者广泛使用,我们希望满足不同开发者的需求。Devicetree 还拥有相对强大的生态系统。
- 目前,这些 API 的编写方式不适合多组织协作。
- 它们会导致固件和板级驱动程序在检测系统中可用的硬件时重复工作。
- 在支持多个相关开发板(即共享 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 中的描述进行解释。
Devicetree 的作用
设备树仅用于描述硬件布局信息。必须通过产品组装完成产品配置。无论开发板运行的是哪款产品,设备树都应仅包含与开发板相关的真实信息,而不是配置可能因产品而异的驱动程序行为,例如预分配的内存缓冲区大小或分区映射。
Devicetree 与 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 路径。
设备树的可审核性
为了便于审核设备树,我们还将引入设备树“金标准”文件。这些金文件将是将设备树源代码编译为二进制文件,然后再编译回源代码的输出。这样,源代码更改对最终设备树的影响就会很明显。编译开发板驱动程序和设备树后,我们将编译和反编译设备树源代码,如果输出与金标准不同,则构建将失败。
例如,如果多个开发板包含一个通用的设备树源文件(例如由 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 上:
- compatible - compatible 数组中的第一个值将作为绑定属性
fuchsia.devicetree.first_compatible
公开。将来,当更复杂的绑定系统(具有优先级)可用时,可能会使用其他值。- 此外,我们将允许节点定义自己的绑定属性,可能通过命名空间型属性(例如,
bind,<prop-name> = <value>
对应于值为value
的绑定属性prop-name
)。
- 此外,我们将允许节点定义自己的绑定属性,可能通过命名空间型属性(例如,
- phandle - 用于标识设备树中的设备节点的唯一标识符。由设备树编译器生成,仅在运行时使用。
- status - 可在开发板驱动程序中使用,用于控制是否发布节点。在实现此 RFC 期间,我们将调查是否应从受支持的绑定中省略 status,如果这样做可以显著提高设备树源文件的清晰度,我们可能会选择省略它。
- 具体而言,我们建议将设备树源文件组合在一起(将单个 SoC 定义拆分到多个文件中,并仅在最终的开发板文件中添加必要的文件),而不是将它们添加到一个文件中,并通过 status 属性停用/启用 IP 块。
- #address-cells、#size-cells - 将在开发板驱动程序中用于确定其他值的大小。
- reg - 对于可从树的根地址到的节点,将由板级驱动程序用于确定物理内存区域。这些内存区域将通过
GetMmio(int index)
FIDL 调用(可能通过平台总线驱动程序)提供给子节点。 - range - 在开发板驱动程序中用于确定子地址范围如何映射到父级。
中断
中断绑定将遵循规范第 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
的属性。这些 fragment 父项最终将命名为 gpio-<name>NNN
。如果命名了多个 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
节点,并将0xffe09000...0xffe090a0
中的 MMIO 区域添加到平台设备的定义中。 - 查看中断资源,并向设备组添加中断节点,其中包含中断部分中所述的属性。
- 查看时钟资源,并向设备组添加时钟节点,其中包含时钟部分中所述的属性。
如需绑定到此触摸屏设备,设备驱动程序的复合绑定文件将如下所示:
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"
可以停用/启用节点)的复杂性。
此外,还没有出现明显的 devicetree 替代方案(大多数操作系统都使用 devicetree 或板级文件来解决此问题)。设备树自 1994 年以来就已存在,并已成为描述硬件的首选方式。
替代方案:定义 Fuchsia 平台强制要求的稳定设备树架构
我们可以定义一个由单个通用“devicetree”开发板驱动程序支持的单个设备树架构,并将其作为使用设备树的官方方式。
这将使我们更接近为所有使用设备树的系统提供单个 Fuchsia 映像的目标,但设备树的差异很大(与 ACPI 等相比),这使得这项工作非常繁重,并且可能需要进行大量的演变。
本 RFC 并未排除未来采用此类架构的可能性,但我们认为,目前采用设备树的最实用方法是上述方法。
替代方案:通过更广泛的生态系统来稳定设备树绑定
我们可以与使用设备树的其他团队合作,在更广泛的生态系统中实现绑定标准化。
这可能需要数年时间才能完成,不在本 RFC 的讨论范围内。仅仅因为绝大多数设备树都不是为 Fuchsia 编写的,Fuchsia 就不可能领导此类工作。
备选方案:自行构建 DSL
我们可以自行开发与 FIDL 集成并绑定规则的 DSL,而不是使用设备树。这可能是我们日后要做的事,但在撰写本文时,我们还没有关于此 DSL 的具体实现想法。如果我们日后确实决定需要 DSL,那么在实现此 RFC 时获得的教训很可能会对其设计产生重要影响。
在先技术和参考文档
- Linux 和设备树 - 简要介绍 Linux 如何使用设备树。
- 适用于 ARM 的设备树 - 这是 2009 年的一场演讲,当时 Linux 正在考虑为 ARM 采用设备树。
- Devicetree:过去、现在和未来 - 2019 年的一场演讲,介绍了 Linux 上设备树的状态。
- devicetree.org - devicetree 规范组织。
- ACPI 与 DT - 介绍如何在 Linux 中统一 ACPI 和设备树接口的演讲。