Flattened Device Tree (FDT) Essential for Virtualization Developers
This article is not a formal introduction or spec of Flattened Device Tree (FDT), but is a collection of practical skills and experience. They are mainly from the view of a virtualization developer on arm64 architecture.
What is FDT?
FDT is a data structure, describing the hardware configuration of a machine.
This is not the formal definition of FDT, but it is practical enough for developers to understand why it exists.
Before FDT came around, the hardware information (like the starting address and size of the RAM, or where a UART device is, etc) was written in source code. You can imagine, a lot of source code was written only to define the resources of a particular machine, and these code cannot be shared between different machines. This problem was then serious on arm-based platform due to architecture reason.
FDT was invented to separate source code and resource definition. Source code is only for functionality; while resources are described in a tree-like data structure.
Now you can download the latest specification for FDT from devicetree.org
.
The advantage of introducing FDT includes at least:
- Source code for different devices drivers and platforms can be shared easily
- The presentation of resources is uniformed
- The new designed tree-view makes it easy to understand what hardware a machine owns
The content of a typical FDT is like:
/{
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
memory {
device_type = "memory";
reg = <0x00000000 0x40000000>;
};
......
The basic elements of a device tree are:
- Node: A node stands for a device or a collection of devices.
memory
in the previous example is a node. The whole itself is also node (root node). - Attribute: Key-value pair used to describe the node.
Several sorts of artifacts are related to FDT:
.dts
: Device tree source file. This is the human readable source file. The content piece shown above is picked from a.dts
file. People create.dts
files for there own machines, write the device node and specify the resources..dtsi
: You may find many.dtsi
files under Linux kernel source folderarch/arm64/boot/dts/
. They are the common “header files” of device tree source. They are included in other.dts
files..dtb
: Device tree blob file. This is the FDT binary built from.dts
. Kernel reads and parses.dtb
file to understand hardware structure.dtc
:dtc
is not part of a device tree itself, but the most commonly used tool to encode and decode device trees on Linux.
How to observe FDT?
Decoding a `.dtb` file
If you can get the .dtb
file you are interested in. Decoding is as easy as running the command:
# dtc -I dtb -O dts -o device-tree.dts device-tree.dtb
The command means: Input (-I
) is a device tree blob file (dtb
): “device-tree.dtb”, output (-O
)is a device tree source file (dts
): “device-tree.dts”. Then you can check the content of .dts
with any text editor.
On a running system
If you want to check the device tree of a running operating system, you can get the input from the kernel file system:
# dtc -I fs /sys/firmware/devicetree/base
From a VMM
If you are using a virtual machine. Besides the 2 ways just mentioned, the virtual machine monitor (VMM) may provide additional support to observe FDT.
Cloud Hypervisor prints the decoded FDT content to its log file. The FDT content is output in DEBUG level, it’s only dumped with the most verbose logging level. If you enable -vvv
option, you can see the FDT text in log file, like:
How to build FDT?
Encoding a `.dts` file
This is the most typical way. After preparing a device tree source file, you can generate the blob file with:
# dtc -I dts -O dtb -o device-tree.dtb device-tree.dts
This is the invert process of the decoding.
In a VMM
Using libfdt
dtc
tool provides a library libfdt
which contains a rich set of APIs for building FDT. In this header file you can have a glance of all the functions to be used in creating a FDT.
For a VMM written in C language, using libfdt
looks the most natural and convenient way. That is the case of kvmtool
.
To a Rust based VMM developer, there are 2 ways to build FDT.
libfdt
wrapper
Cloud Hypervisor used a libfdt
wrapper to create FDT in early stage. You can see how is the FDT generated in this source file. That’s almost the same thing as using libfdt
itself, just written in Rust language.
A piece of code for example:
The drawback of this method is obvious: the build environment is not independent, it requires a native library.
At that time, all 3 major Rust based VMMs (Cloud Hypervisor, Firecracker and CrosVM) works with FDT in the same way.
Rust crate for FDT
Later, a Google engineer contributed a FDT crate in Rust language to rust-vmm project: vmm-fdt.
With the FDT crate, now the code creating the same node looks like:
The overall process is quite similar. But now we are saved from using external library. It helps avoid a lot of trouble in setting up development environment, especially in CI (continuous integration).