What is Compiling Linux Device Trees-Free Embedded Linux Course

Compiling Linux Device Trees

A free embedded Linux course lecture from EmbeddedPathashala

dtbs target explained
DTC compiler internals
Original board walkthrough

A kernel image alone can’t boot most ARM boards — it also needs a device tree, the binary description of exactly what hardware is present and how it’s wired up. This lecture, part of our free linux kernel development course, covers how the kernel build system compiles device tree source files into the .dtb binaries your bootloader hands to the kernel, and what to do when that step goes wrong. If you’re working through this as part of a broader free embedded systems course, this is the natural follow-on to building the kernel image itself.

device tree
dtbs
dtc
.dts / .dtsi
overlays
free embedded linux course

What You Will Learn

  • What a device tree is for and why the kernel can’t hardcode hardware layout instead
  • How the dtbs build target finds and compiles device tree source files
  • The relationship between .dts, .dtsi, and the compiled .dtb
  • How to build device trees for your board and verify the result
  • Common device tree build and boot failures, and how to debug them

Prerequisites

  • A kernel source tree already configured for your target ARCH (see our kbuild lecture)
  • Basic familiarity with how kbuild locates architecture-specific files under arch/$ARCH/
  • The device-tree-compiler package installed, or the in-tree dtc that kbuild builds automatically

Why Device Trees Exist

Early embedded ARM support in Linux hardcoded board details directly into C files — one file per board, describing every peripheral address, IRQ number, and GPIO wiring in code. That approach didn’t scale once ARM SoC vendors started shipping dozens of board variants a year. The device tree moved that description out of code and into data: a plain-text source format (.dts) that describes the hardware as a tree of nodes and properties, compiled into a compact binary (.dtb) that the bootloader passes to the kernel at boot time. The same kernel binary can then run on many boards, picking its hardware configuration from whichever .dtb was loaded rather than from board-specific code paths.

A minimal, original example — a fictional board node describing one UART and one LED, written the way you’d find it in a real .dts file:

/dts-v1/;
/ {
    model = "EP Devboard Rev A";
    compatible = "ep,devboard";

    soc {
        uart0: serial@4a000000 {
            compatible = "ep,ep-uart";
            reg = <0x4a000000 0x1000>;
            interrupts = <38>;
            status = "okay";
        };
    };

    leds {
        compatible = "gpio-leds";
        status_led {
            gpios = <&gpio1 5 0>;
            label = "ep:status";
        };
    };
};

None of this needs to change if you swap the SoC’s silicon revision — only the device tree does, and the kernel driver for ep,ep-uart is matched purely through the compatible string.

The dtbs Build Target

Kbuild treats device tree compilation as its own target, separate from the kernel image itself. Running it looks like this:

$ make ARCH=arm64 dtbs

This walks the rules defined in arch/$ARCH/boot/dts/Makefile, which lists which .dts files belong to that architecture (often grouped by vendor subdirectory — arch/arm64/boot/dts/<vendor>/). Each listed source file is run through the device tree compiler, dtc, producing one .dtb per board in the same directory as its source:

$ make ARCH=arm64 dtbs
  DTC     arch/arm64/boot/dts/ep/ep-devboard-a.dtb
  DTC     arch/arm64/boot/dts/ep/ep-devboard-b.dtb
  DTC     arch/arm64/boot/dts/ep/ep-devboard-c.dtb

You can also target a single board directly, which is faster during iterative development than rebuilding every .dtb in the tree:

$ make ARCH=arm64 arch/arm64/boot/dts/ep/ep-devboard-a.dtb

dts, dtsi, And dtb: What Each File Actually Is

Extension Role
.dts Device Tree Source — one file per board, the top-level description you compile
.dtsi Device Tree Source Include — shared fragments (an SoC’s common peripherals) pulled in with #include, so multiple boards sharing the same chip don’t duplicate every node
.dtb Device Tree Blob — the compiled binary your bootloader loads and passes to the kernel
.dtbo Device Tree Blob Overlay — a compiled fragment applied on top of a base dtb at boot or runtime, common for add-on boards/HATs

A typical SoC vendor’s .dtsi describes everything common to that chip family — CPU cores, memory controller, standard peripheral blocks — and each board’s .dts includes it, then adds or overrides only what’s specific to that board, like which UART is wired to a debug header or which GPIO drives a status LED.

Verifying A Compiled dtb

Because a .dtb is binary, you can’t just read it — but dtc can decompile it back to readable source for inspection, which is useful for confirming what actually got baked into the binary versus what you expected from the source:

$ dtc -I dtb -O dts arch/arm64/boot/dts/ep/ep-devboard-a.dtb | less

This round-trip is one of the fastest ways to catch a mistake like a node accidentally disabled with status = "disabled", or an overlay that didn’t apply the way you expected.

Device Tree Build And Boot Flow
board.dts –includes–> soc.dtsi
|
v
dtc compiler (make ARCH=… dtbs)
|
v
board.dtb (binary blob)
|
v
bootloader loads dtb + kernel image
|
v
kernel parses dtb –> builds internal device tree –> matches drivers via “compatible”

Real-World Use Case: Bringing Up A New Board Revision

Say your team spins a new hardware revision that moves the debug UART to a different pin and adds a second LED. With the device tree model, that change lives entirely in one new .dts file that includes the existing SoC .dtsi and overrides just the two changed nodes — no kernel or driver source changes, no separate kernel build. You add the new file to the vendor’s Makefile list, run make ARCH=arm64 dtbs, flash the new .dtb alongside the existing kernel image, and the same kernel now boots correctly on both revisions.

Common Mistakes And Troubleshooting

Symptom Likely cause Fix
New board’s .dtb never appears in the build output Board file not added to arch/$ARCH/boot/dts/<vendor>/Makefile Add the .dtb target explicitly to that Makefile’s dtb-y list
Kernel boots but a peripheral driver never probes Node’s status property left as "disabled", or compatible string mismatch Decompile the built dtb with dtc -I dtb -O dts and check the node directly
dtc warnings about missing #address-cells/#size-cells A container node’s addressing properties weren’t inherited or set Explicitly set #address-cells/#size-cells on the parent node
Board hangs immediately after “Starting kernel…” Wrong or mismatched dtb passed by the bootloader for that kernel/board combination Confirm the bootloader’s dtb load command points at the correct, freshly built file

Best Practices

  • Keep shared SoC details in a .dtsi and board-specific details in the .dts — don’t duplicate common nodes across boards
  • Use overlays (.dtbo) for optional add-on hardware instead of maintaining separate full .dts files per configuration
  • Decompile your built .dtb after any node change to confirm the compiled result matches your intent
  • Give new nodes clear, vendor-prefixed compatible strings to avoid collisions with upstream driver matches

Security Considerations

A device tree is trusted, unauthenticated input to the kernel on most embedded boot chains — if an attacker can substitute the dtb, they can potentially remap memory regions or disable protections a driver relies on. On products where firmware integrity matters, verify the dtb alongside the kernel image using your bootloader’s signature/verification mechanism rather than treating it as inert configuration data.

Summary And Key Takeaways

  • Device trees separate hardware description from kernel code, letting one kernel binary support many boards
  • make ARCH=$ARCH dtbs compiles every listed .dts into a .dtb using the in-tree dtc compiler
  • .dtsi files hold shared SoC-level detail; .dts files hold board-specific detail; .dtb is the compiled result the bootloader hands to the kernel
  • Decompiling a built .dtb is the fastest way to confirm what actually got compiled

Conclusion

Between this lecture and the previous one on building kernel images with kbuild, you now have the complete picture of what a bootloader needs to start Linux on an ARM board: a boot-ready kernel image and a matching device tree blob. Understanding both halves — and how kbuild builds each one independently — is one of the more practical, immediately useful skills covered in this free linux device drivers course, especially once you start bringing up your own board revisions.

Frequently Asked Questions

Why can’t the kernel just detect hardware automatically instead of using a device tree?

Many embedded SoC peripherals aren’t self-describing the way PCI or USB devices are — there’s no bus protocol to query for what’s attached. The device tree exists specifically to supply that information for hardware that can’t announce itself.

What’s the difference between a dts and a dtsi file?

A .dts is a complete, board-level source file you compile directly. A .dtsi is an include fragment, typically holding SoC-common nodes, that one or more .dts files pull in with #include to avoid duplication.

Do I need to rebuild the kernel image after changing a device tree?

No — the dtbs target is independent of the kernel image target. You can rebuild just the .dtb and reflash it alongside your existing kernel image.

How do I check what’s actually inside a compiled dtb?

Run dtc in reverse: dtc -I dtb -O dts yourfile.dtb decompiles the binary back into readable source so you can inspect exactly what was compiled.

What are device tree overlays used for?

Overlays let you apply a small device tree fragment on top of a base dtb, typically for optional add-on hardware, without maintaining a full separate .dts for every possible hardware combination.

My new board’s dtb isn’t being built — why?

The most common cause is forgetting to list the new .dtb target in the vendor’s arch/$ARCH/boot/dts/<vendor>/Makefile. Kbuild only compiles device trees it’s explicitly told about.

 

Keep Building Your Embedded Linux Skills

More free lectures on kernel internals, device drivers, and Bluetooth stack development are waiting for you on EmbeddedPathashala.

 

Leave a Reply

Your email address will not be published. Required fields are marked *