Also referred to as Devicetree Blob (DTB). It is a flat binary encoding of data (primarily devicetree data, although other data is possible as well). The data is internally stored as a tree of named nodes and properties. Nodes contain properties and child nodes, while properties are name-value pairs.
The Devicetree Blobs (.dtb files) are compiled from the Devicetree Source
files (.dts) through the Devicetree compiler (DTC).
On Linux systems that support this, the blobs can be accessed in
/sys/firmware/fdt:
The encoding of strings used in the strings_block and structure_block is
actually a subset of ASCII:
https://devicetree-specification.readthedocs.io/en/v0.3/devicetree-basics.html#node-names
Example files:
This page hosts a formal specification of Flattened Devicetree Format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
"io"
)
/**
* Also referred to as Devicetree Blob (DTB). It is a flat binary encoding
* of data (primarily devicetree data, although other data is possible as well).
* The data is internally stored as a tree of named nodes and properties. Nodes
* contain properties and child nodes, while properties are name-value pairs.
*
* The Devicetree Blobs (`.dtb` files) are compiled from the Devicetree Source
* files (`.dts`) through the Devicetree compiler (DTC).
*
* On Linux systems that support this, the blobs can be accessed in
* `/sys/firmware/fdt`:
*
* * <https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-firmware-ofw>
*
* The encoding of strings used in the `strings_block` and `structure_block` is
* actually a subset of ASCII:
*
* <https://devicetree-specification.readthedocs.io/en/v0.3/devicetree-basics.html#node-names>
*
* Example files:
*
* * <https://github.com/qemu/qemu/tree/master/pc-bios>
* @see <a href="https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html">Source</a>
* @see <a href="https://elinux.org/images/f/f4/Elc2013_Fernandes.pdf">Source</a>
*/
type Dtb_Fdt int
const (
Dtb_Fdt__BeginNode Dtb_Fdt = 1
Dtb_Fdt__EndNode Dtb_Fdt = 2
Dtb_Fdt__Prop Dtb_Fdt = 3
Dtb_Fdt__Nop Dtb_Fdt = 4
Dtb_Fdt__End Dtb_Fdt = 9
)
var values_Dtb_Fdt = map[Dtb_Fdt]struct{}{1: {}, 2: {}, 3: {}, 4: {}, 9: {}}
func (v Dtb_Fdt) isDefined() bool {
_, ok := values_Dtb_Fdt[v]
return ok
}
type Dtb struct {
Magic []byte
TotalSize uint32
OfsStructureBlock uint32
OfsStringsBlock uint32
OfsMemoryReservationBlock uint32
Version uint32
MinCompatibleVersion uint32
BootCpuidPhys uint32
LenStringsBlock uint32
LenStructureBlock uint32
_io *kaitai.Stream
_root *Dtb
_parent kaitai.Struct
_raw_memoryReservationBlock []byte
_raw_stringsBlock []byte
_raw_structureBlock []byte
_f_memoryReservationBlock bool
memoryReservationBlock *Dtb_MemoryBlock
_f_stringsBlock bool
stringsBlock *Dtb_Strings
_f_structureBlock bool
structureBlock *Dtb_FdtBlock
}
func NewDtb() *Dtb {
return &Dtb{
}
}
func (this Dtb) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb) Read(io *kaitai.Stream, parent kaitai.Struct, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp1 = tmp1
this.Magic = tmp1
if !(bytes.Equal(this.Magic, []uint8{208, 13, 254, 237})) {
return kaitai.NewValidationNotEqualError([]uint8{208, 13, 254, 237}, this.Magic, this._io, "/seq/0")
}
tmp2, err := this._io.ReadU4be()
if err != nil {
return err
}
this.TotalSize = uint32(tmp2)
tmp3, err := this._io.ReadU4be()
if err != nil {
return err
}
this.OfsStructureBlock = uint32(tmp3)
tmp4, err := this._io.ReadU4be()
if err != nil {
return err
}
this.OfsStringsBlock = uint32(tmp4)
tmp5, err := this._io.ReadU4be()
if err != nil {
return err
}
this.OfsMemoryReservationBlock = uint32(tmp5)
tmp6, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Version = uint32(tmp6)
tmp7, err := this._io.ReadU4be()
if err != nil {
return err
}
this.MinCompatibleVersion = uint32(tmp7)
if !(this.MinCompatibleVersion <= this.Version) {
return kaitai.NewValidationGreaterThanError(this.Version, this.MinCompatibleVersion, this._io, "/seq/6")
}
tmp8, err := this._io.ReadU4be()
if err != nil {
return err
}
this.BootCpuidPhys = uint32(tmp8)
tmp9, err := this._io.ReadU4be()
if err != nil {
return err
}
this.LenStringsBlock = uint32(tmp9)
tmp10, err := this._io.ReadU4be()
if err != nil {
return err
}
this.LenStructureBlock = uint32(tmp10)
return err
}
func (this *Dtb) MemoryReservationBlock() (v *Dtb_MemoryBlock, err error) {
if (this._f_memoryReservationBlock) {
return this.memoryReservationBlock, nil
}
this._f_memoryReservationBlock = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(this.OfsMemoryReservationBlock), io.SeekStart)
if err != nil {
return nil, err
}
tmp11, err := this._io.ReadBytes(int(this.OfsStructureBlock - this.OfsMemoryReservationBlock))
if err != nil {
return nil, err
}
tmp11 = tmp11
this._raw_memoryReservationBlock = tmp11
_io__raw_memoryReservationBlock := kaitai.NewStream(bytes.NewReader(this._raw_memoryReservationBlock))
tmp12 := NewDtb_MemoryBlock()
err = tmp12.Read(_io__raw_memoryReservationBlock, this, this._root)
if err != nil {
return nil, err
}
this.memoryReservationBlock = tmp12
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.memoryReservationBlock, nil
}
func (this *Dtb) StringsBlock() (v *Dtb_Strings, err error) {
if (this._f_stringsBlock) {
return this.stringsBlock, nil
}
this._f_stringsBlock = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(this.OfsStringsBlock), io.SeekStart)
if err != nil {
return nil, err
}
tmp13, err := this._io.ReadBytes(int(this.LenStringsBlock))
if err != nil {
return nil, err
}
tmp13 = tmp13
this._raw_stringsBlock = tmp13
_io__raw_stringsBlock := kaitai.NewStream(bytes.NewReader(this._raw_stringsBlock))
tmp14 := NewDtb_Strings()
err = tmp14.Read(_io__raw_stringsBlock, this, this._root)
if err != nil {
return nil, err
}
this.stringsBlock = tmp14
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.stringsBlock, nil
}
func (this *Dtb) StructureBlock() (v *Dtb_FdtBlock, err error) {
if (this._f_structureBlock) {
return this.structureBlock, nil
}
this._f_structureBlock = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(this.OfsStructureBlock), io.SeekStart)
if err != nil {
return nil, err
}
tmp15, err := this._io.ReadBytes(int(this.LenStructureBlock))
if err != nil {
return nil, err
}
tmp15 = tmp15
this._raw_structureBlock = tmp15
_io__raw_structureBlock := kaitai.NewStream(bytes.NewReader(this._raw_structureBlock))
tmp16 := NewDtb_FdtBlock()
err = tmp16.Read(_io__raw_structureBlock, this, this._root)
if err != nil {
return nil, err
}
this.structureBlock = tmp16
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.structureBlock, nil
}
type Dtb_FdtBeginNode struct {
Name string
Padding []byte
_io *kaitai.Stream
_root *Dtb
_parent *Dtb_FdtNode
}
func NewDtb_FdtBeginNode() *Dtb_FdtBeginNode {
return &Dtb_FdtBeginNode{
}
}
func (this Dtb_FdtBeginNode) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_FdtBeginNode) Read(io *kaitai.Stream, parent *Dtb_FdtNode, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp17, err := this._io.ReadBytesTerm(0, false, true, true)
if err != nil {
return err
}
this.Name = string(tmp17)
tmp19, err := this._io.Pos()
if err != nil {
return err
}
tmp18 := -(tmp19) % 4
if tmp18 < 0 {
tmp18 += 4
}
tmp20, err := this._io.ReadBytes(int(tmp18))
if err != nil {
return err
}
tmp20 = tmp20
this.Padding = tmp20
return err
}
type Dtb_FdtBlock struct {
Nodes []*Dtb_FdtNode
_io *kaitai.Stream
_root *Dtb
_parent *Dtb
}
func NewDtb_FdtBlock() *Dtb_FdtBlock {
return &Dtb_FdtBlock{
}
}
func (this Dtb_FdtBlock) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_FdtBlock) Read(io *kaitai.Stream, parent *Dtb, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 1;; i++ {
tmp21 := NewDtb_FdtNode()
err = tmp21.Read(this._io, this, this._root)
if err != nil {
return err
}
_it := tmp21
this.Nodes = append(this.Nodes, _it)
if _it.Type == Dtb_Fdt__End {
break
}
}
return err
}
type Dtb_FdtNode struct {
Type Dtb_Fdt
Body kaitai.Struct
_io *kaitai.Stream
_root *Dtb
_parent *Dtb_FdtBlock
}
func NewDtb_FdtNode() *Dtb_FdtNode {
return &Dtb_FdtNode{
}
}
func (this Dtb_FdtNode) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_FdtNode) Read(io *kaitai.Stream, parent *Dtb_FdtBlock, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp22, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Type = Dtb_Fdt(tmp22)
switch (this.Type) {
case Dtb_Fdt__BeginNode:
tmp23 := NewDtb_FdtBeginNode()
err = tmp23.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp23
case Dtb_Fdt__Prop:
tmp24 := NewDtb_FdtProp()
err = tmp24.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp24
}
return err
}
type Dtb_FdtProp struct {
LenProperty uint32
OfsName uint32
Property []byte
Padding []byte
_io *kaitai.Stream
_root *Dtb
_parent *Dtb_FdtNode
_f_name bool
name string
}
func NewDtb_FdtProp() *Dtb_FdtProp {
return &Dtb_FdtProp{
}
}
func (this Dtb_FdtProp) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_FdtProp) Read(io *kaitai.Stream, parent *Dtb_FdtNode, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp25, err := this._io.ReadU4be()
if err != nil {
return err
}
this.LenProperty = uint32(tmp25)
tmp26, err := this._io.ReadU4be()
if err != nil {
return err
}
this.OfsName = uint32(tmp26)
tmp27, err := this._io.ReadBytes(int(this.LenProperty))
if err != nil {
return err
}
tmp27 = tmp27
this.Property = tmp27
tmp29, err := this._io.Pos()
if err != nil {
return err
}
tmp28 := -(tmp29) % 4
if tmp28 < 0 {
tmp28 += 4
}
tmp30, err := this._io.ReadBytes(int(tmp28))
if err != nil {
return err
}
tmp30 = tmp30
this.Padding = tmp30
return err
}
func (this *Dtb_FdtProp) Name() (v string, err error) {
if (this._f_name) {
return this.name, nil
}
this._f_name = true
tmp31, err := this._root.StringsBlock()
if err != nil {
return "", err
}
thisIo := tmp31._io
_pos, err := thisIo.Pos()
if err != nil {
return "", err
}
_, err = thisIo.Seek(int64(this.OfsName), io.SeekStart)
if err != nil {
return "", err
}
tmp32, err := thisIo.ReadBytesTerm(0, false, true, true)
if err != nil {
return "", err
}
this.name = string(tmp32)
_, err = thisIo.Seek(_pos, io.SeekStart)
if err != nil {
return "", err
}
return this.name, nil
}
type Dtb_MemoryBlock struct {
Entries []*Dtb_MemoryBlockEntry
_io *kaitai.Stream
_root *Dtb
_parent *Dtb
}
func NewDtb_MemoryBlock() *Dtb_MemoryBlock {
return &Dtb_MemoryBlock{
}
}
func (this Dtb_MemoryBlock) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_MemoryBlock) Read(io *kaitai.Stream, parent *Dtb, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0;; i++ {
tmp33, err := this._io.EOF()
if err != nil {
return err
}
if tmp33 {
break
}
tmp34 := NewDtb_MemoryBlockEntry()
err = tmp34.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Entries = append(this.Entries, tmp34)
}
return err
}
type Dtb_MemoryBlockEntry struct {
Address uint64
Size uint64
_io *kaitai.Stream
_root *Dtb
_parent *Dtb_MemoryBlock
}
func NewDtb_MemoryBlockEntry() *Dtb_MemoryBlockEntry {
return &Dtb_MemoryBlockEntry{
}
}
func (this Dtb_MemoryBlockEntry) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_MemoryBlockEntry) Read(io *kaitai.Stream, parent *Dtb_MemoryBlock, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp35, err := this._io.ReadU8be()
if err != nil {
return err
}
this.Address = uint64(tmp35)
tmp36, err := this._io.ReadU8be()
if err != nil {
return err
}
this.Size = uint64(tmp36)
return err
}
/**
* physical address of a reserved memory region
*/
/**
* size of a reserved memory region
*/
type Dtb_Strings struct {
Strings []string
_io *kaitai.Stream
_root *Dtb
_parent *Dtb
}
func NewDtb_Strings() *Dtb_Strings {
return &Dtb_Strings{
}
}
func (this Dtb_Strings) IO_() *kaitai.Stream {
return this._io
}
func (this *Dtb_Strings) Read(io *kaitai.Stream, parent *Dtb, root *Dtb) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0;; i++ {
tmp37, err := this._io.EOF()
if err != nil {
return err
}
if tmp37 {
break
}
tmp38, err := this._io.ReadBytesTerm(0, false, true, true)
if err != nil {
return err
}
this.Strings = append(this.Strings, string(tmp38))
}
return err
}