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.
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 = "...";
}; |
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.
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:
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).
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.
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 { ... }; ... }; |
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.
The following instructions walk you through a sample implementation of DTO
with libufdt (sample code below).
libufdt, include
libfdt for data structures and APIs:
#include <libfdt.h> #include <ufdt_overlay.h>
.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);
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;
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);merged_fdt to get the size of
dtc_totalsize():
merged_fdt_size = dtc_totalsize(merged_fdt);
my_kernel_entry(0, machine_type, merged_fdt);
#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); }
Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
Last updated 2026-04-10 UTC.