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.
import kaitai_struct_nim_runtime
import options
type
Dtb* = ref object of KaitaiStruct
`magic`*: seq[byte]
`totalSize`*: uint32
`ofsStructureBlock`*: uint32
`ofsStringsBlock`*: uint32
`ofsMemoryReservationBlock`*: uint32
`version`*: uint32
`minCompatibleVersion`*: uint32
`bootCpuidPhys`*: uint32
`lenStringsBlock`*: uint32
`lenStructureBlock`*: uint32
`parent`*: KaitaiStruct
`rawMemoryReservationBlockInst`*: seq[byte]
`rawStringsBlockInst`*: seq[byte]
`rawStructureBlockInst`*: seq[byte]
`memoryReservationBlockInst`: Dtb_MemoryBlock
`memoryReservationBlockInstFlag`: bool
`stringsBlockInst`: Dtb_Strings
`stringsBlockInstFlag`: bool
`structureBlockInst`: Dtb_FdtBlock
`structureBlockInstFlag`: bool
Dtb_Fdt* = enum
begin_node = 1
end_node = 2
prop = 3
nop = 4
end = 9
Dtb_FdtBeginNode* = ref object of KaitaiStruct
`name`*: string
`padding`*: seq[byte]
`parent`*: Dtb_FdtNode
Dtb_FdtBlock* = ref object of KaitaiStruct
`nodes`*: seq[Dtb_FdtNode]
`parent`*: Dtb
Dtb_FdtNode* = ref object of KaitaiStruct
`type`*: Dtb_Fdt
`body`*: KaitaiStruct
`parent`*: Dtb_FdtBlock
Dtb_FdtProp* = ref object of KaitaiStruct
`lenProperty`*: uint32
`ofsName`*: uint32
`property`*: seq[byte]
`padding`*: seq[byte]
`parent`*: Dtb_FdtNode
`nameInst`: string
`nameInstFlag`: bool
Dtb_MemoryBlock* = ref object of KaitaiStruct
`entries`*: seq[Dtb_MemoryBlockEntry]
`parent`*: Dtb
Dtb_MemoryBlockEntry* = ref object of KaitaiStruct
`address`*: uint64
`size`*: uint64
`parent`*: Dtb_MemoryBlock
Dtb_Strings* = ref object of KaitaiStruct
`strings`*: seq[string]
`parent`*: Dtb
proc read*(_: typedesc[Dtb], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Dtb
proc read*(_: typedesc[Dtb_FdtBeginNode], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_FdtNode): Dtb_FdtBeginNode
proc read*(_: typedesc[Dtb_FdtBlock], io: KaitaiStream, root: KaitaiStruct, parent: Dtb): Dtb_FdtBlock
proc read*(_: typedesc[Dtb_FdtNode], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_FdtBlock): Dtb_FdtNode
proc read*(_: typedesc[Dtb_FdtProp], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_FdtNode): Dtb_FdtProp
proc read*(_: typedesc[Dtb_MemoryBlock], io: KaitaiStream, root: KaitaiStruct, parent: Dtb): Dtb_MemoryBlock
proc read*(_: typedesc[Dtb_MemoryBlockEntry], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_MemoryBlock): Dtb_MemoryBlockEntry
proc read*(_: typedesc[Dtb_Strings], io: KaitaiStream, root: KaitaiStruct, parent: Dtb): Dtb_Strings
proc memoryReservationBlock*(this: Dtb): Dtb_MemoryBlock
proc stringsBlock*(this: Dtb): Dtb_Strings
proc structureBlock*(this: Dtb): Dtb_FdtBlock
proc name*(this: Dtb_FdtProp): string
##[
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>
]##
proc read*(_: typedesc[Dtb], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Dtb =
template this: untyped = result
this = new(Dtb)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
let magicExpr = this.io.readBytes(int(4))
this.magic = magicExpr
let totalSizeExpr = this.io.readU4be()
this.totalSize = totalSizeExpr
let ofsStructureBlockExpr = this.io.readU4be()
this.ofsStructureBlock = ofsStructureBlockExpr
let ofsStringsBlockExpr = this.io.readU4be()
this.ofsStringsBlock = ofsStringsBlockExpr
let ofsMemoryReservationBlockExpr = this.io.readU4be()
this.ofsMemoryReservationBlock = ofsMemoryReservationBlockExpr
let versionExpr = this.io.readU4be()
this.version = versionExpr
let minCompatibleVersionExpr = this.io.readU4be()
this.minCompatibleVersion = minCompatibleVersionExpr
let bootCpuidPhysExpr = this.io.readU4be()
this.bootCpuidPhys = bootCpuidPhysExpr
let lenStringsBlockExpr = this.io.readU4be()
this.lenStringsBlock = lenStringsBlockExpr
let lenStructureBlockExpr = this.io.readU4be()
this.lenStructureBlock = lenStructureBlockExpr
proc memoryReservationBlock(this: Dtb): Dtb_MemoryBlock =
if this.memoryReservationBlockInstFlag:
return this.memoryReservationBlockInst
let pos = this.io.pos()
this.io.seek(int(this.ofsMemoryReservationBlock))
let rawMemoryReservationBlockInstExpr = this.io.readBytes(int(this.ofsStructureBlock - this.ofsMemoryReservationBlock))
this.rawMemoryReservationBlockInst = rawMemoryReservationBlockInstExpr
let rawMemoryReservationBlockInstIo = newKaitaiStream(rawMemoryReservationBlockInstExpr)
let memoryReservationBlockInstExpr = Dtb_MemoryBlock.read(rawMemoryReservationBlockInstIo, this.root, this)
this.memoryReservationBlockInst = memoryReservationBlockInstExpr
this.io.seek(pos)
this.memoryReservationBlockInstFlag = true
return this.memoryReservationBlockInst
proc stringsBlock(this: Dtb): Dtb_Strings =
if this.stringsBlockInstFlag:
return this.stringsBlockInst
let pos = this.io.pos()
this.io.seek(int(this.ofsStringsBlock))
let rawStringsBlockInstExpr = this.io.readBytes(int(this.lenStringsBlock))
this.rawStringsBlockInst = rawStringsBlockInstExpr
let rawStringsBlockInstIo = newKaitaiStream(rawStringsBlockInstExpr)
let stringsBlockInstExpr = Dtb_Strings.read(rawStringsBlockInstIo, this.root, this)
this.stringsBlockInst = stringsBlockInstExpr
this.io.seek(pos)
this.stringsBlockInstFlag = true
return this.stringsBlockInst
proc structureBlock(this: Dtb): Dtb_FdtBlock =
if this.structureBlockInstFlag:
return this.structureBlockInst
let pos = this.io.pos()
this.io.seek(int(this.ofsStructureBlock))
let rawStructureBlockInstExpr = this.io.readBytes(int(this.lenStructureBlock))
this.rawStructureBlockInst = rawStructureBlockInstExpr
let rawStructureBlockInstIo = newKaitaiStream(rawStructureBlockInstExpr)
let structureBlockInstExpr = Dtb_FdtBlock.read(rawStructureBlockInstIo, this.root, this)
this.structureBlockInst = structureBlockInstExpr
this.io.seek(pos)
this.structureBlockInstFlag = true
return this.structureBlockInst
proc fromFile*(_: typedesc[Dtb], filename: string): Dtb =
Dtb.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_FdtBeginNode], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_FdtNode): Dtb_FdtBeginNode =
template this: untyped = result
this = new(Dtb_FdtBeginNode)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
let nameExpr = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
this.name = nameExpr
let paddingExpr = this.io.readBytes(int(-(this.io.pos) %%% 4))
this.padding = paddingExpr
proc fromFile*(_: typedesc[Dtb_FdtBeginNode], filename: string): Dtb_FdtBeginNode =
Dtb_FdtBeginNode.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_FdtBlock], io: KaitaiStream, root: KaitaiStruct, parent: Dtb): Dtb_FdtBlock =
template this: untyped = result
this = new(Dtb_FdtBlock)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
block:
var i: int
while true:
let it = Dtb_FdtNode.read(this.io, this.root, this)
this.nodes.add(it)
if it.type == dtb.end:
break
inc i
proc fromFile*(_: typedesc[Dtb_FdtBlock], filename: string): Dtb_FdtBlock =
Dtb_FdtBlock.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_FdtNode], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_FdtBlock): Dtb_FdtNode =
template this: untyped = result
this = new(Dtb_FdtNode)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
let typeExpr = Dtb_Fdt(this.io.readU4be())
this.type = typeExpr
block:
let on = this.type
if on == dtb.begin_node:
let bodyExpr = Dtb_FdtBeginNode.read(this.io, this.root, this)
this.body = bodyExpr
elif on == dtb.prop:
let bodyExpr = Dtb_FdtProp.read(this.io, this.root, this)
this.body = bodyExpr
proc fromFile*(_: typedesc[Dtb_FdtNode], filename: string): Dtb_FdtNode =
Dtb_FdtNode.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_FdtProp], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_FdtNode): Dtb_FdtProp =
template this: untyped = result
this = new(Dtb_FdtProp)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
let lenPropertyExpr = this.io.readU4be()
this.lenProperty = lenPropertyExpr
let ofsNameExpr = this.io.readU4be()
this.ofsName = ofsNameExpr
let propertyExpr = this.io.readBytes(int(this.lenProperty))
this.property = propertyExpr
let paddingExpr = this.io.readBytes(int(-(this.io.pos) %%% 4))
this.padding = paddingExpr
proc name(this: Dtb_FdtProp): string =
if this.nameInstFlag:
return this.nameInst
let io = Dtb(this.root).stringsBlock.io
let pos = io.pos()
io.seek(int(this.ofsName))
let nameInstExpr = encode(io.readBytesTerm(0, false, true, true), "ASCII")
this.nameInst = nameInstExpr
io.seek(pos)
this.nameInstFlag = true
return this.nameInst
proc fromFile*(_: typedesc[Dtb_FdtProp], filename: string): Dtb_FdtProp =
Dtb_FdtProp.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_MemoryBlock], io: KaitaiStream, root: KaitaiStruct, parent: Dtb): Dtb_MemoryBlock =
template this: untyped = result
this = new(Dtb_MemoryBlock)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
block:
var i: int
while not this.io.isEof:
let it = Dtb_MemoryBlockEntry.read(this.io, this.root, this)
this.entries.add(it)
inc i
proc fromFile*(_: typedesc[Dtb_MemoryBlock], filename: string): Dtb_MemoryBlock =
Dtb_MemoryBlock.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_MemoryBlockEntry], io: KaitaiStream, root: KaitaiStruct, parent: Dtb_MemoryBlock): Dtb_MemoryBlockEntry =
template this: untyped = result
this = new(Dtb_MemoryBlockEntry)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
##[
physical address of a reserved memory region
]##
let addressExpr = this.io.readU8be()
this.address = addressExpr
##[
size of a reserved memory region
]##
let sizeExpr = this.io.readU8be()
this.size = sizeExpr
proc fromFile*(_: typedesc[Dtb_MemoryBlockEntry], filename: string): Dtb_MemoryBlockEntry =
Dtb_MemoryBlockEntry.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Dtb_Strings], io: KaitaiStream, root: KaitaiStruct, parent: Dtb): Dtb_Strings =
template this: untyped = result
this = new(Dtb_Strings)
let root = if root == nil: cast[Dtb](this) else: cast[Dtb](root)
this.io = io
this.root = root
this.parent = parent
block:
var i: int
while not this.io.isEof:
let it = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
this.strings.add(it)
inc i
proc fromFile*(_: typedesc[Dtb_Strings], filename: string): Dtb_Strings =
Dtb_Strings.read(newKaitaiFileStream(filename), nil, nil)