In the world of embedded Linux, the Device Tree (DT) serves as the central blueprint describing hardware components to the operating system. While a static device tree blob (DTB) is compiled and loaded at boot to initialize peripherals and buses, there are scenarios in which the hardware configuration of a system changes dynamically after the kernel has already started. This is where Device Tree Overlays (DTOs) come into play. Overlays are an extension of the device tree concept, allowing developers to modify or extend the existing hardware description at runtime without rebooting. This capability enables a far more flexible approach to hardware management, opening up possibilities for runtime hardware reconfiguration, conditional peripheral enablement, modular expansion boards, and adaptive driver loading based on live hardware changes. In modern embedded systems where development boards often rely on plug-and-play daughterboards or hot-swappable interfaces, overlays have become an essential tool to avoid downtime and simplify the development cycle.
The core idea behind a Device Tree Overlay is that it contains only the fragment of a device tree necessary to describe the changes or additions to the existing hardware description. The kernel’s device tree subsystem can then merge this overlay into the currently active device tree, effectively “patching” the hardware description in real time. The overlay is written in the same Device Tree Source (DTS) format as the main device tree but uses special syntax to reference the nodes and properties it intends to modify. For example, if you have a base board running an SoC with an unused SPI bus, and you plug in an SPI-based display at runtime, you can load a corresponding overlay to describe the display’s driver binding, bus parameters, GPIO lines, and interrupt assignments—instantly enabling the device without a reboot.
To compile overlays, developers use the same Device Tree Compiler (DTC) as for normal device trees, but with additional parameters. A typical process involves writing the overlay as a .dts file, compiling it into a .dtbo (Device Tree Blob Overlay) binary, and then loading it into the kernel through a mechanism supported by the platform—this might be the configfs interface, the dtoverlay command (on Raspberry Pi), or a specialized bootloader-assisted overlay loading system. The .dtbo file is not a standalone hardware description; rather, it contains references (symbols) to nodes in the base DTB, ensuring that only the intended properties are added or altered.
On systems running a recent mainline Linux kernel with CONFIG_OF_OVERLAY enabled, overlays can be loaded at runtime using configfs. The process starts by mounting configfs if it is not already mounted. This is typically done using:
sudo mount -t configfs none /sys/kernel/configOnce configfs is available, a directory for overlays can be found at /sys/kernel/config/device-tree/overlays/. To apply an overlay, you create a subdirectory (named however you wish) and copy the compiled .dtbo file into a special dtbo file inside that subdirectory:
sudo mkdir /sys/kernel/config/device-tree/overlays/spi_display
sudo cat spi_display.dtbo > /sys/kernel/config/device-tree/overlays/spi_display/dtboIf the overlay is compatible and properly references existing nodes, the kernel will merge it instantly. Any device drivers corresponding to the newly added nodes will be probed automatically by the driver model, and the hardware should become active. Conversely, removing the overlay directory will unbind the devices and revert the change:
sudo rmdir /sys/kernel/config/device-tree/overlays/spi_displayWhat makes this mechanism powerful is that it integrates directly with the kernel’s device model. A correctly applied overlay triggers the same driver binding logic as a boot-time device tree, meaning there is no functional difference between a device described at boot and one described dynamically. This capability is especially beneficial for prototyping hardware interfaces, testing various driver configurations, or supporting optional expansion modules in production systems.
From a development perspective, writing a good overlay requires understanding both the structure of the base device tree and the specifics of the hardware to be added. It is often necessary to inspect the base DTB for the running system, which can be extracted from /proc/device-tree or from the bootloader’s firmware image. A common approach is to dump the current device tree into a .dts file for reference:
dtc -I dtb -O dts -o running.dts /proc/device-treeWith this reference in hand, you can identify the bus nodes, available GPIO lines, and compatible strings needed for your overlay. For example, if adding a new I²C temperature sensor, the overlay might target the existing I²C bus node, appending a child node with its address, compatible string, and any relevant power supply bindings. If the overlay also modifies existing nodes—such as enabling an I²C bus that is disabled by default—special overlay syntax is used to ensure the property is merged rather than replaced.
The syntax of overlays includes fragment blocks, each defining a target or target-path for modification, and an __overlay__ node containing the new or modified properties. For instance, a simple overlay to enable and configure an SPI device might look like this in source form:
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&spi1>;
__overlay__ {
status = "okay";
display@0 {
compatible = "sitronix,st7789v";
reg = <0>; /* CS 0 */
spi-max-frequency = <32000000>;
dc-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
};
};
};
};After compiling this into a .dtbo and loading it, the kernel will bring up the SPI display without touching the rest of the system.
However, overlays are not without challenges. One key limitation is that they cannot completely remove nodes or delete properties from the base device tree—only add or modify. In cases where you need to disable hardware or drastically restructure the hardware description, a full device tree rebuild and reboot may still be necessary. Additionally, the kernel’s overlay support requires proper symbol export in the base device tree; without these, references from overlays to base nodes will fail. This makes it important to work closely with the board’s base DTS when planning to support runtime overlays.
In production systems, overlays can be leveraged for modular hardware designs. For example, in industrial control systems, an embedded computer might have multiple daughterboard options for field-specific I/O. Instead of maintaining a unique firmware image for each hardware configuration, the system can ship with a base DTB and a set of .dtbo files, applying the appropriate overlay during initialization based on detected hardware. This reduces maintenance complexity and accelerates deployment in environments where hardware may change during the lifecycle of the product.
Security considerations also arise with runtime overlays. Since they can modify hardware descriptions at a fundamental level, overlay loading is typically restricted to privileged users. On systems with multi-tenant configurations or sensitive applications, improperly controlled overlay loading could allow malicious users to manipulate device bindings, potentially enabling rogue hardware access or disabling critical devices. Therefore, overlay loading interfaces should be tightly controlled, with kernel configuration and file system permissions set appropriately.
Beyond embedded boards, overlays also find use in development environments, particularly for testing drivers without a full reboot cycle. Driver developers can simulate hot-plug scenarios by loading overlays for virtual or loopback devices, allowing iterative testing without hardware power cycles. This is particularly valuable when working with buses like SPI and I²C, where the driver’s binding and probing behavior must be verified repeatedly under different configurations.
One practical example is testing an I²C EEPROM driver. By preparing an overlay that describes an I²C EEPROM at a given bus address, the driver developer can load and unload it repeatedly, watching kernel logs to verify correct initialization and cleanup:
sudo dmesg --follow
sudo mkdir /sys/kernel/config/device-tree/overlays/eeprom_test
sudo cat eeprom_test.dtbo > /sys/kernel/config/device-tree/overlays/eeprom_test/dtboUpon loading, dmesg will typically show a log indicating the driver binding. Removing the overlay will trigger an unbind message, confirming proper teardown.
As kernel support for overlays continues to evolve, additional features such as better error reporting, property deletion support, and improved tooling for overlay composition are likely to make this already powerful feature even more useful. In the meantime, mastering Device Tree Overlays provides embedded Linux developers with a significant advantage in building systems that are both flexible and maintainable. By allowing the hardware description to adapt dynamically to real-world changes, overlays bridge the gap between fixed hardware design and the adaptable nature of software, making them a cornerstone of modern embedded Linux development.
The flexibility of overlays extends beyond simple hardware add-ons to scenarios like hot-swappable displays, dynamic FPGA reconfiguration, and even SoC feature toggling. On platforms where FPGAs are used for custom accelerators, it is possible to load a new FPGA bitstream and immediately apply a matching overlay that describes the new registers, DMA channels, or interrupts associated with that design. This dynamic pairing of reconfigurable logic with corresponding Device Tree modifications enables extremely powerful and adaptable embedded systems, capable of changing their hardware personalities on demand.
Debugging overlay-related issues often involves a mix of kernel logs, Device Tree introspection, and driver tracing. After applying an overlay, inspecting /proc/device-tree or using the fdtdump tool can confirm whether the expected nodes and properties are present in the live tree. Kernel messages from dmesg can provide insights into driver probe sequences triggered by the overlay, including errors if devices fail to initialize. In development, it is common to iterate on overlays multiple times, adjusting node paths, property values, or binding compatibility strings to ensure correct driver matching.
For developers working with overlays extensively, automation is key. Scripts can manage the detection of hardware modules, selection of matching overlays, verification of their integrity, and application via configfs. Combined with systemd units or udev rules, overlays can be applied transparently to the user, making hardware reconfiguration feel seamless. This is particularly powerful in consumer-facing devices, where end users may swap hardware without ever realizing that a kernel-level reconfiguration is taking place behind the scenes.
In conclusion, advanced Device Tree Overlays represent one of the most significant steps forward in making Linux truly dynamic in the embedded space. They bridge the gap between static boot-time hardware descriptions and the real-world need for runtime adaptability, enabling more modular designs, faster prototyping, and easier maintenance. The journey from writing a .dts overlay file to deploying a robust runtime reconfiguration system requires a solid grasp of kernel internals, careful attention to modularity and security, and an awareness of the specific hardware behaviours involved. As embedded Linux continues to expand into fields like IoT, robotics, and industrial automation, overlays will become an increasingly indispensable tool for building flexible, maintainable, and resilient systems.
