Home  >  Article  >  System Tutorial  >  Linux device tree dts transplantation: how to describe and transfer hardware configuration information

Linux device tree dts transplantation: how to describe and transfer hardware configuration information

WBOY
WBOYforward
2024-02-13 11:27:12970browse

In Linux systems, the device tree is a tree data structure that describes hardware configuration. It originates from the Open Firmware standard and is used to provide an interface between operating system software and hardware to start and run the system. The device tree can reduce the changes required to the kernel to support new hardware, improve code reuse, accelerate the development of Linux support packages, and enable a single kernel image to support multiple systems. This article will introduce the basic steps and methods of Linux device tree dts transplantation, including the device tree's data storage format, source code description syntax, U-Boot and Linux kernel support and parsing process for device trees, etc.

Linux device tree dts transplantation: how to describe and transfer hardware configuration information

Keywords: flat device tree; DTS; PowerPC; Linux

Servers from IBM, Sun and other manufacturers initially used Firmware (a program embedded in hardware devices, with
(used to provide an interface between software and hardware), used to initialize system configuration, and provide an interface between operating system software and hardware
port to get the system up and running. Later, for standardization and compatibility, IBM, Sun, etc. jointly launched the firmware interface IEEE 1275
standards, so that their servers such as IBM PowerPC pSeries, Apple PowerPC, Sun SPARC, etc. all use Open
Firmware builds the device tree information of the system hardware at runtime and passes it to the kernel to start the system [1]. so
The benefits include reducing the kernel's heavy reliance on system hardware, accelerating the development of support packages, and reducing changes caused by hardware
It reduces the demand and cost and reduces the requirements for kernel design and compilation.

With the development of the Linux/ppc64 kernel, the kernel code has gradually migrated from the original arch/ppc32 and arch/ppc64 to
Unify the arch/powerpc directory, and introduce the Open Firmware API in the kernel code to use the standard firmware interface [2].
When the Linux kernel is running, it needs to know some relevant information about the hardware. For
compiled with ARCH=powerpc parameter Kernel image, this information needs to be based on the Open Firmware specification and exist in the form of a device tree [3]. In this way, the kernel is starting
Read and scan the device tree provided by Open Firmware at runtime to obtain the platform's hardware device information and search for matching devices
device driver and bind the driver to the device.

In embedded PowerPC, system boot codes such as U-Boot are generally used instead of Open Firmware.
Early U-Boot used the static data structure struct bd_t in include/asm-ppc/u-boot.h to transfer basic information about the board
Hand it to the kernel, which handles the rest. Such an interface is not flexible enough. If the hardware changes, you need to re-customize the compilation and burning
Write boot code and kernel, and it is no longer suitable for the current kernel. In order to adapt to the development of the kernel and embedded PowerPC
Due to the ever-changing platform, U-Boot introduces the flat device tree FDT by absorbing the advantages of standard Open Firmware. Dynamic interface, using a separate FDT blob (binary large object, a container that can store binary files)
Stores parameters passed to the kernel [3]. Some certain information, such as cache size, interrupt routing, etc., are provided directly by the device tree,
Other information, such as eTSEC's MAC address, frequency, PCI bus number, etc., are modified by U-Boot at runtime.
U-Boot replaces bd_t with a flat device tree, and backward compatibility with bd_t is no longer guaranteed.

2 Device tree concept Simply put, the device tree is a tree-shaped data structure that describes hardware configuration, with one and only one root node [4]. It contains
Contains information about CPU, physical memory, bus, serial port, PHY and other peripheral devices. The tree inherits Open
Firmware IEEE 1275 device tree definition. The operating system can syntax analyze this structure at startup and configure it
Set up the kernel and load the corresponding driver.

3 Device tree storage format U-Boot needs to pass the storage address of the device tree in memory to the kernel. The tree is mainly composed of three parts: Head
(Header), structure block (Structure block), Strings block (Strings block). Storage of device tree in memory
Storage layout diagram 1 is as follows:
Figure 1 Device tree storage format diagram
Fig1 The layout of a DT block

3.1 Header The header mainly describes the basic information of the device tree, such as the device tree magic number flag, device tree block size, and offset address of the structure block
etc. Its specific structure boot_param_header is as follows. The values ​​in this structure are expressed in big-endian mode and offset
The address is calculated relative to the starting address of the device tree head.

3.2 Structure block The flat device tree structure block is a linearized tree structure. Together with the string block, it forms the main body of the device tree, starting with node
Save the device information of the target board in the form. In the structure block, the node start flag is the constant macro OF_DT_BEGIN_NODE,
The node end mark is the macro OF_DT_END_NODE; the child nodes are defined before the node end mark. A node can be summarized
It starts with OF_DT_BEGIN_NODE, includes the node path, attribute list, child node list, and ends with
OF_DT_END_NODE ends the sequence, and each child node itself has a similar structure.

3.3 Strings block
In order to save space, some attribute names, especially those that appear repeatedly and redundantly, are extracted and stored separately
to a string block. This block contains a number of attribute name strings with end flags.
is stored in the structure block of the device tree The offset addresses of these strings, so that the attribute name string can be easily found. The introduction of string blocks saves embedding
The storage space of the built-in system is tight.

4 Device tree source code DTS representation
The device tree source code file (.dts) describes the system hardware configuration device tree in a readable and editable text format, supporting C/C
Comments on the method, the structure has a unique root node "/", each node has its own name and can contain multiple
child node. The data format of the device tree follows the Open Firmware IEEE standard 1275. This article only briefly describes the
of the device tree. For data layout and syntax, Linux board support package developers should refer to the IEEE 1275 standard [5] and other documents [2] [4] for details.
To illustrate, first give an example of the device tree source code of a minimal system based on the PowerPC MPC8349E processor.
As you can see, there are many nodes in this device tree, and each node has a node unit name specified. Each attribute is followed by
Give the corresponding value. The content enclosed in double quotes is an ASCII string, and the content enclosed in angle brackets is a 32-bit hexadecimal
value. This tree structure is a simplified collection of nodes and attributes required to start the Linux kernel, including the basic mode of the root node
Information, CPU and physical memory layout, it also includes command line argument information passed to the kernel through the /chosen node.

/ {
model = "MPC8349EMITX";
compatible = "MPC8349EMITX", "MPC834xMITX", "MPC83xxMITX";
\#address-cells = ; /* 32bit address */
\#size-cells = ; /* 4GB size */
cpus {
\#address-cells = ;
\#size-cells = ;
PowerPC,8349@0 {
device_type = "cpu";
reg = ;
d-cache-line-size = ; /* 32 Bytes */
i-cache-line-size = ;
d-cache-size = ; /* L1 dcache, 32K */
i-cache-size = ;
timebase-frequency = ; /* from bootloader */
bus-frequency = ;
clock-frequency = ;
};
};
memory {
device_type = "memory";
reg = ; /* 256MB */
};
chosen {
name = "chosen";
bootargs = "root=/dev/ram rw console=ttyS0,115200";
linux,stdout-path = "/soc8349@e0000000/serial@4500";
};
};

4.1 Root node
The starting point of the device tree is called the root node "/". The attribute model specifies the name of the target board platform or module, attribute
The compatible value indicates the name of a compatible development board in the same series as the target board. For most 32-bit platforms, properties
The values ​​of #address-cells and #size-cells are generally 1.

4.2 CPU Node
The /cpus node is a child node of the root node, and there is a corresponding node for each CPU in the system. /cpus node
There are no required properties, but it is a good practice to specify #address-cells = and #size-cells =, which also refer to
Understand the reg attribute format of each CPU node to facilitate numbering of physical CPUs.

This node should contain the properties of each CPU on the board. The CPU name is generally written as PowerPC, such as
Freescale will use PowerPC,8349 to describe the MPC8349E processor in this article. The unit name of the CPU node should be
In the format of cpu@0, this node generally needs to specify device_type (fixed to "cpu"), and the entry of the first-level data/instruction cache
Size, size of the first-level data/instruction cache, core, bus clock frequency, etc. Booting through the system in the example above
The code dynamically fills in clock frequency related items.

4.3 System memory node
This node is used to describe the physical memory range on the target board. It is generally called the /memory node. There can be one or more nodes.
When there are multiple nodes, they need to be followed by the unit address to distinguish them; when there is only one unit address, the unit address does not need to be written,
Default is 0.

This node contains the attributes of the physical memory on the board. Generally, device_type (fixed to "memory") and reg
must be specified. Attributes. The attribute value of reg is given in the form of, as shown in the above example, the starting point of the target board memory
The address is 0 and the size is 256M bytes.

4.4 /chosen node
This node is a little special. Usually, Open Firmware stores variable environment information, such as parameters,
Default input and output device.
The bootargs and linux, stdout-path attribute values ​​are generally specified in this node. The bootargs attribute is set to be passed to the kernel
Argument string for kernel command line. linux, stdout-path is often the node path name of a standard terminal device, and the kernel will use this as
as the default terminal.

U-Boot added support for flat device tree FDT after version 1.3.0, U-Boot loads the Linux kernel,
After the Ramdisk file system (if used) and the device tree binary are mirrored to physical memory, execute Linux
after booting Before the kernel, it modifies the device tree binary. It will populate the device tree with necessary information such as MAC address,
Number of PCI buses, etc. U-Boot will also fill in the "/chosen" node in the device tree file, including serial port, root
Device (Ramdisk, hard disk or NFS boot) and other related information.

4.5 System on Chip SOC Node
This node is used to describe the system on chip SOC. If the processor is a SOC, this node must exist. Top SOC Section
The information contained in the dot is visible to all devices on this SOC. The node name should contain the unit address of this SOC, which is this SOC
The base address of the memory mapped register. The SOC node name is named in the form of /soc, such as MPC8349's SOC
The node is "soc8349".

在属性中应该指定device_type(固定为”soc”)、ranges、bus-frequency 等属性。ranges
属性值以的形式指定。SOC 节点还包含目标板使用的每个
SOC 设备子节点,应该在设备树中尽可能详细地描述此SOC 上的外围设备。如下给出带有
看门狗设备的SOC 节点DTS 示例。

soc8349@e0000000 {
\#address-cells = ;
\#size-cells = ;
device_type = "soc";
compatible = "simple-bus";
ranges = ; /* size 1MB */
reg = ;
bus-frequency = ; /* from bootloader */
{
device_type = "watchdog";
compatible = "mpc83xx_wdt";
reg = ; /* offset: 0x200 */
};
};

4.6 其他设备节点
分级节点用来描述系统上的总线和设备,类似物理总线拓扑,能很方便的描述设备间的
关系。对于系统上的每个总线和设备,在设备树中都有其节点。对于这些设备属性的描述和
定义请详细参考IEEE 1275 标准及本文参考文献[2]。

设备树的中断系统稍显复杂,设备节点利用interrupt-parent 和interrupts 属性描述到中
断控制器的中断连接。其中interrupt-parent 属性值为中断控制器节点的指针,#interrupts 属
性值描述可触发的中断信号,其值格式与中断控制器的interrupt-cells 属性值有关。一般
#interrupt-cells 属性值为2,interrupts 属性就对应为一对描述硬件中断号和中断触发方式的
十六进制值。

5 扁平设备树编译
根据嵌入式板的设备信息写设备树源码文件(.dts)通常比较简单,但是手写二进制的
扁平设备树(.dtb)就显得比较复杂了。设备树编译器dtc 就是用来根据设备树源码的文本
文件生成设备树二进制镜像的。dtc 编译器会对输入文件进行语法和语义检查,并根据Linux
内核的要求检查各节点及属性,将设备树源码文件(.dts)编译二进制文件(.dtb),以保证
内核能正常启动。dtc 编译器的使用方法如下所示[6]:
dtc [ -I dts ] [ -O dtb ] [ -o opt_file ] [ -V opt_version ] ipt_file
2.6.25 版本之后的内核源码已经包含了dtc 编译器。在配置编译内核时选中
CONFIG_DTC,会自动生成设备树编译器dtc。将编写的目标板设备树文件mpc8349emitx.dts
放到内核源码的arch/powerpc/boot/dts/目录下,利用内核Makefile 生成blob 的简单规则,使
用以下命令亦可完成设备树的dtc 编译:
$ make mpc8349emitx.dtb

6 U-Boot 相关设置说明
为使 U-Boot 支持设备树,需要在板子配置头文件中设置一系列宏变量。如本文在

MPC8349E 处理器目标板中移植的U-Boot 配置如下:
/* pass open firmware flat tree */
\#define CONFIG_OF_LIBFDT 1
\#undef CONFIG_OF_FLAT_TREE
\#define CONFIG_OF_BOARD_SETUP 1
\#define CONFIG_OF_HAS_BD_T 1
\#define CONFIG_OF_HAS_UBOOT_ENV 1
启动引导代码U-Boot 在完成自己的工作之后,会加载Linux 内核,并将扁平设备树的
地址传递给内核,其代码形式如下:
\#if defined(CONFIG_OF_FLAT_TREE) || defined(CONFIG_OF_LIBFDT)
if (of_flat_tree) { /* device tree; boot new style */
/*
\* Linux Kernel Parameters (passing device tree):
\* r3: pointer to the fdt, followed by the board info data
\* r4: physical pointer to the kernel itself
\* r5: NULL
\* r6: NULL
\* r7: NULL
*/
(*kernel) ((bd_t *)of_flat_tree, (ulong)kernel, 0, 0, 0);
/* does not return */
}
\#endif

arch/powerpc 内核的入口有且只有一个,入口点为内核镜像的起始。此入口支持两种调
用方式,一种是支持Open Firmware 启动,另一种对于没有OF 的引导代码,需要使用扁平
设备树块,如上示例代码。寄存器r3 保存指向设备树的物理地址指针,寄存器r4 保存为内
核在物理内存中的地址,r5 为NULL。其中的隐含意思为:假设开启了mmu,那么这个mmu
的映射关系是1:1 的映射,即虚拟地址和物理地址是相同的。

7 Linux 内核对设备树的解析
扁平设备树描述了目标板平台中的设备树信息。每个设备都有一个节点来描述其信息,
每个节点又可以有子节点及其相应的属性。内核源码中include/linux/of.h 及drivers/of/base.c
等文件中提供了一些Open Firmware API,通过这些API,内核及设备驱动可以查找到相应
的设备节点,读取其属性值,利用这些信息正确地初始化和驱动硬件。

图2 内核及驱动对扁平设备树的解析
Fig2 Interaction from kernel and drivers with the FDT blob

8 结论
通过本文,你应该对Linux设备树dts移植有了一个基本的了解,它是一种描述和传递硬件配置信息的有效方式,可以适应嵌入式Linux系统的多样化需求。当然,设备树也不是一成不变的,它需要根据具体的硬件平台和内核版本进行定制和修改。总之,设备树是Linux系统中不可或缺的一个组件,值得你深入学习和掌握。

The above is the detailed content of Linux device tree dts transplantation: how to describe and transfer hardware configuration information. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete