This page discusses optimizations you can make to your device tree overlay (DTO) implementation, describes restrictions against overlaying the root node, and details how to configure compressed overlays in the DTBO image. It also provides sample implementation instructions and code.
Kernel command line
The original kernel command line in device tree (DT) is located in the
chosen/bootargs
node. The bootloader must concatenate this
location with other sources of kernel command line:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO cannot concatenate values from main DT and overlay DT, so
you must put the kernel command line of the main DT in
chosen/bootargs
and the kernel command line of the overlay DT in
chosen/bootargs_ext
. Bootloader can then concatenate these
locations and pass the result to the kernel.
main.dts | overlay.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
libufdt
While the latest
libfdt
supports DTO, is it recommended to use libufdt
to implement DTO
(AOSP source at
platform/system/libufdt
).
libufdt
builds a real tree structure (un-flattened device tree,
or ufdt) from the flattened device tree (FDT), so it can improve the
merging of two .dtb
files from O(N2) to O(N), where N is the
number of nodes in the tree.
Performance testing
In Google's internal testing, using libufdt
on 2405
.dtb
and 283 .dtbo
DT nodes results in file sizes of
70,618 and 8,566 bytes after compilation. Compared with a
DTO
implementation ported from FreeBSD (124 ms runtime), libufdt
DTO runtime is 10 ms.
Performance testing for Pixel devices compared libufdt
and
libfdt
. The number of base nodes effect is similar, but includes
the following differences:
- 500 overlay (append or override) operations have 6x to 8x time difference
- 1000 overlay (append or override) operations have 8x to 10x time difference
Example with appending count set to X:
Figure 1. Appending count is X.
Example with overriding count set to X:
Figure 2. Overriding count is X.
libufdt
is developed with some libfdt
APIs and data
structures. When using libufdt
, you must include and link
libfdt
(however, in your code you can use the libfdt
API to operate DTB or DTBO).
libufdt DTO API
The main API to DTO in libufdt
is as follows:
struct fdt_header *ufdt_apply_overlay( struct fdt_header *main_fdt_header, size_t main_fdt_size, void *overlay_fdt, size_t overlay_size);
The parameter main_fdt_header
is the main DT and
overlay_fdt
is the buffer containing the contents of a
.dtbo
file. The return value is a new buffer containing the
merged DT (or null
in case of error). The merged DT is formatted
in FDT, which you can pass to the kernel when starting the kernel.
The new buffer from the return value is created by dto_malloc()
,
which you should implement when porting libufdt
into bootloader.
For reference implementations, refer to
sysdeps/libufdt_sysdeps_*.c
.
Root node restrictions
You cannot overlay a new node or property into the root node of main DT because overlay operations rely on labels. Because the main DT must define a label and the overlay DT assigns the nodes to be overlaid with labels, you cannot give a label for the root node (and therefore cannot overlay the root node).
SoC vendors must define the overlaying ability of main DT; ODM/OEMs can only
append or override nodes with labels defined by the SoC vendor. As a
workaround, you can define an odm
node under the
root node in base DT, enabling all ODM nodes in overlay DT to add new nodes.
Alternatively, you could put all SoC-related nodes in the base DT into an
soc
node under root node as described below:
main.dts | overlay.dts |
---|---|
/dts-v1/; / { compatible = "corp,bar"; ... chosen: chosen { bootargs = "..."; }; /* nodes for all soc nodes */ soc { ... soc_device@0: soc_device@0 { compatible = "corp,bar"; ... }; ... }; odm: odm { /* reserved for overlay by odm */ }; }; |
/dts-v1/; /plugin/; / { }; &chosen { bootargs_ex = "..."; }; &odm { odm_device@0 { ... }; ... }; |
Use compressed overlays
Android 9 adds support for using compressed overlays in the DTBO image when using version 1 of the DT table header. When using DTBO header v1, the four least significant bits of the flags field in dt_table_entry indicate the compression format of the DT entry.
struct dt_table_entry_v1 { uint32_t dt_size; uint32_t dt_offset; /* offset from head of dt_table_header */ uint32_t id; /* optional, must be zero if unused */ uint32_t rev; /* optional, must be zero if unused */ uint32_t flags; /* For version 1 of dt_table_header, the 4 least significant bits of 'flags' are used to indicate the compression format of the DT entry as per the enum 'dt_compression_info' */ uint32_t custom[3]; /* optional, must be zero if unused */ };
Currently, zlib
and gzip
compressions are supported.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Android 9 adds support for testing compressed
overlays to the VtsFirmwareDtboVerification
test to help you
verify the correctness of overlay app.
Sample DTO implementation
The following instructions walk you through a sample implementation of DTO
with libufdt
(sample code below).
Sample DTO instructions
- Include libraries. To use
libufdt
, includelibfdt
for data structures and APIs:#include <libfdt.h> #include <ufdt_overlay.h>
- Load main DT and overlay DT. Load
.dtb
and.dtbo
from storage into memory (exact steps depend on your design). At this point, you should have the buffer and size of.dtb
/.dtbo
:main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- Overlay the DTs:
- Use
ufdt_install_blob()
to get the FDT header for main DT:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Call
ufdt_apply_overlay()
to DTO to get a merged DT in FDT format:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Use
merged_fdt
to get the size ofdtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Pass the merged DT to start the kernel:
my_kernel_entry(0, machine_type, merged_fdt);
- Use
Sample DTO code
#include <libfdt.h> #include <ufdt_overlay.h> … { struct fdt_header *main_fdt_header; struct fdt_header *merged_fdt; /* load main dtb into memory and get the size */ main_size = my_load_main_dtb(main_buf, main_buf_size); /* load overlay dtb into memory and get the size */ overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size); /* overlay */ main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size; merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size); merged_fdt_size = dtc_totalsize(merged_fdt); /* pass to kernel */ my_kernel_entry(0, machine_type, merged_fdt); }