The header starts the blob at offset 0. It is ten 32-bit words, all big-endian, and says where the other three blocks are.
The fields are from the Devicetree Specification, section 5.2; the header size of each version is from libfdt
(fdt.h, fdt_header_size_), which is part of the dtc source.
Fields
| Offset | Size | Field | Version | What it holds |
|---|---|---|---|---|
0x00 | 4 | magic | 1 | 0xD00DFEED. |
0x04 | 4 | totalsize | 1 | The size of the whole blob in bytes: header, the three blocks and any free space between them. |
0x08 | 4 | off_dt_struct | 1 | The offset of the structure block, from the start of the blob. A multiple of 4. |
0x0C | 4 | off_dt_strings | 1 | The offset of the strings block. |
0x10 | 4 | off_mem_rsvmap | 1 | The offset of the memory reservation block. A multiple of 8. |
0x14 | 4 | version | 1 | The version of the layout: 17 for the current one. A boot program writes 17 or later. |
0x18 | 4 | last_comp_version | 1 | The oldest version this one stays compatible with. For version 17 it is 16, and a boot program always writes 16. |
0x1C | 4 | boot_cpuid_phys | 2 | The physical ID of the CPU the system boots on: the value in that CPU node’s reg. |
0x20 | 4 | size_dt_strings | 3 | The size of the strings block in bytes. |
0x24 | 4 | size_dt_struct | 17 | The size of the structure block in bytes. |
Header size by version
A reader takes the header’s size from the version. Version 1 has seven words (28 bytes), version 2 adds
boot_cpuid_phys (32 bytes), versions 3 to 16 add size_dt_strings (36 bytes) and version 17 adds
size_dt_struct (40 bytes). libfdt reads versions 2 to 17. A reader of version 17 accepts any blob whose
last_comp_version is 17 or less. Versions below 16 differ in the structure block.
Alignment and layout
The blob itself must be loaded at an 8-byte aligned address. The memory reservation block is 8-byte aligned, the structure block 4-byte aligned, and the strings block has no alignment. The blocks follow the header in the order reservation, structure, strings, and free space is allowed between them. Only the offsets and sizes in the header say where they really are, so the viewer reads them from there and shows any gap as unused space.
Example
The start of bcm2710-rpi-zero-2-w.dtb, the Raspberry Pi Zero 2 W blob from Raspberry Pi OS (kernel 6.18):
00000000: d00d feed 0000 83ac 0000 0048 0000 762c
00000010: 0000 0028 0000 0011 0000 0010 0000 0000
00000020: 0000 0d80 0000 75e4
The total size is 0x83AC (33,708 bytes, the size of the file), the structure block is at 0x48, the
strings block at 0x762C, the reservation block at 0x28, the version is 17 (0x11), the
last compatible version is 16 (0x10), the boot CPU is 0, the strings block is 0xD80 (3,456) bytes
and the structure block 0x75E4 (30,180) bytes. The header’s 40 bytes end at 0x28, where the
reservation block begins, so there is no free space.
What the viewer checks
- the magic, and that
totalsizefits the file (a larger one means the blob is cut short, a smaller one that bytes follow it); - that each block lies inside the total size, that the structure block is a multiple of 4 in size and offset, and that the reservation block is 8-byte aligned;
- that the blocks do not overlap each other;
- the version rules: nothing below 2, nothing above 17 (read as 17),
last_comp_versionnot above 17 or above the version, and 16 for version 17; - that
boot_cpuid_physis theregof one of the/cpuschildren, when the tree has any.
A problem is listed and the blob is shown as far as it can be read: the viewer never refuses a file with the right magic.
A file is recognised by a stricter test: the magic, a totalsize that fits the file and the block offsets inside it.