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.
All parsing code for C++98/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.dtb", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
dtb_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.magic() // => get magic
#ifndef DTB_H_
#define DTB_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* 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>
* \sa https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html Source
* \sa https://elinux.org/images/f/f4/Elc2013_Fernandes.pdf Source
*/
class dtb_t : public kaitai::kstruct {
public:
class memory_block_t;
class fdt_block_t;
class memory_block_entry_t;
class strings_t;
class fdt_prop_t;
class fdt_node_t;
class fdt_begin_node_t;
enum fdt_t {
FDT_BEGIN_NODE = 1,
FDT_END_NODE = 2,
FDT_PROP = 3,
FDT_NOP = 4,
FDT_END = 9
};
dtb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~dtb_t();
class memory_block_t : public kaitai::kstruct {
public:
memory_block_t(kaitai::kstream* p__io, dtb_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~memory_block_t();
private:
std::vector<memory_block_entry_t*>* m_entries;
dtb_t* m__root;
dtb_t* m__parent;
public:
std::vector<memory_block_entry_t*>* entries() const { return m_entries; }
dtb_t* _root() const { return m__root; }
dtb_t* _parent() const { return m__parent; }
};
class fdt_block_t : public kaitai::kstruct {
public:
fdt_block_t(kaitai::kstream* p__io, dtb_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~fdt_block_t();
private:
std::vector<fdt_node_t*>* m_nodes;
dtb_t* m__root;
dtb_t* m__parent;
public:
std::vector<fdt_node_t*>* nodes() const { return m_nodes; }
dtb_t* _root() const { return m__root; }
dtb_t* _parent() const { return m__parent; }
};
class memory_block_entry_t : public kaitai::kstruct {
public:
memory_block_entry_t(kaitai::kstream* p__io, dtb_t::memory_block_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~memory_block_entry_t();
private:
uint64_t m_address;
uint64_t m_size;
dtb_t* m__root;
dtb_t::memory_block_t* m__parent;
public:
/**
* physical address of a reserved memory region
*/
uint64_t address() const { return m_address; }
/**
* size of a reserved memory region
*/
uint64_t size() const { return m_size; }
dtb_t* _root() const { return m__root; }
dtb_t::memory_block_t* _parent() const { return m__parent; }
};
class strings_t : public kaitai::kstruct {
public:
strings_t(kaitai::kstream* p__io, dtb_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~strings_t();
private:
std::vector<std::string>* m_strings;
dtb_t* m__root;
dtb_t* m__parent;
public:
std::vector<std::string>* strings() const { return m_strings; }
dtb_t* _root() const { return m__root; }
dtb_t* _parent() const { return m__parent; }
};
class fdt_prop_t : public kaitai::kstruct {
public:
fdt_prop_t(kaitai::kstream* p__io, dtb_t::fdt_node_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~fdt_prop_t();
private:
bool f_name;
std::string m_name;
public:
std::string name();
private:
uint32_t m_len_property;
uint32_t m_ofs_name;
std::string m_property;
std::string m_padding;
dtb_t* m__root;
dtb_t::fdt_node_t* m__parent;
public:
uint32_t len_property() const { return m_len_property; }
uint32_t ofs_name() const { return m_ofs_name; }
std::string property() const { return m_property; }
std::string padding() const { return m_padding; }
dtb_t* _root() const { return m__root; }
dtb_t::fdt_node_t* _parent() const { return m__parent; }
};
class fdt_node_t : public kaitai::kstruct {
public:
fdt_node_t(kaitai::kstream* p__io, dtb_t::fdt_block_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~fdt_node_t();
private:
fdt_t m_type;
kaitai::kstruct* m_body;
bool n_body;
public:
bool _is_null_body() { body(); return n_body; };
private:
dtb_t* m__root;
dtb_t::fdt_block_t* m__parent;
public:
fdt_t type() const { return m_type; }
kaitai::kstruct* body() const { return m_body; }
dtb_t* _root() const { return m__root; }
dtb_t::fdt_block_t* _parent() const { return m__parent; }
};
class fdt_begin_node_t : public kaitai::kstruct {
public:
fdt_begin_node_t(kaitai::kstream* p__io, dtb_t::fdt_node_t* p__parent = 0, dtb_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~fdt_begin_node_t();
private:
std::string m_name;
std::string m_padding;
dtb_t* m__root;
dtb_t::fdt_node_t* m__parent;
public:
std::string name() const { return m_name; }
std::string padding() const { return m_padding; }
dtb_t* _root() const { return m__root; }
dtb_t::fdt_node_t* _parent() const { return m__parent; }
};
private:
bool f_memory_reservation_block;
memory_block_t* m_memory_reservation_block;
public:
memory_block_t* memory_reservation_block();
private:
bool f_structure_block;
fdt_block_t* m_structure_block;
public:
fdt_block_t* structure_block();
private:
bool f_strings_block;
strings_t* m_strings_block;
public:
strings_t* strings_block();
private:
std::string m_magic;
uint32_t m_total_size;
uint32_t m_ofs_structure_block;
uint32_t m_ofs_strings_block;
uint32_t m_ofs_memory_reservation_block;
uint32_t m_version;
uint32_t m_min_compatible_version;
uint32_t m_boot_cpuid_phys;
uint32_t m_len_strings_block;
uint32_t m_len_structure_block;
dtb_t* m__root;
kaitai::kstruct* m__parent;
std::string m__raw_memory_reservation_block;
kaitai::kstream* m__io__raw_memory_reservation_block;
std::string m__raw_structure_block;
kaitai::kstream* m__io__raw_structure_block;
std::string m__raw_strings_block;
kaitai::kstream* m__io__raw_strings_block;
public:
std::string magic() const { return m_magic; }
uint32_t total_size() const { return m_total_size; }
uint32_t ofs_structure_block() const { return m_ofs_structure_block; }
uint32_t ofs_strings_block() const { return m_ofs_strings_block; }
uint32_t ofs_memory_reservation_block() const { return m_ofs_memory_reservation_block; }
uint32_t version() const { return m_version; }
uint32_t min_compatible_version() const { return m_min_compatible_version; }
uint32_t boot_cpuid_phys() const { return m_boot_cpuid_phys; }
uint32_t len_strings_block() const { return m_len_strings_block; }
uint32_t len_structure_block() const { return m_len_structure_block; }
dtb_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
std::string _raw_memory_reservation_block() const { return m__raw_memory_reservation_block; }
kaitai::kstream* _io__raw_memory_reservation_block() const { return m__io__raw_memory_reservation_block; }
std::string _raw_structure_block() const { return m__raw_structure_block; }
kaitai::kstream* _io__raw_structure_block() const { return m__io__raw_structure_block; }
std::string _raw_strings_block() const { return m__raw_strings_block; }
kaitai::kstream* _io__raw_strings_block() const { return m__io__raw_strings_block; }
};
#endif // DTB_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "dtb.h"
#include "kaitai/exceptions.h"
dtb_t::dtb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_memory_reservation_block = 0;
m__io__raw_memory_reservation_block = 0;
m_structure_block = 0;
m__io__raw_structure_block = 0;
m_strings_block = 0;
m__io__raw_strings_block = 0;
f_memory_reservation_block = false;
f_structure_block = false;
f_strings_block = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::_read() {
m_magic = m__io->read_bytes(4);
if (!(magic() == std::string("\xD0\x0D\xFE\xED", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\xD0\x0D\xFE\xED", 4), magic(), _io(), std::string("/seq/0"));
}
m_total_size = m__io->read_u4be();
m_ofs_structure_block = m__io->read_u4be();
m_ofs_strings_block = m__io->read_u4be();
m_ofs_memory_reservation_block = m__io->read_u4be();
m_version = m__io->read_u4be();
m_min_compatible_version = m__io->read_u4be();
if (!(min_compatible_version() <= version())) {
throw kaitai::validation_greater_than_error<uint32_t>(version(), min_compatible_version(), _io(), std::string("/seq/6"));
}
m_boot_cpuid_phys = m__io->read_u4be();
m_len_strings_block = m__io->read_u4be();
m_len_structure_block = m__io->read_u4be();
}
dtb_t::~dtb_t() {
_clean_up();
}
void dtb_t::_clean_up() {
if (f_memory_reservation_block) {
if (m__io__raw_memory_reservation_block) {
delete m__io__raw_memory_reservation_block; m__io__raw_memory_reservation_block = 0;
}
if (m_memory_reservation_block) {
delete m_memory_reservation_block; m_memory_reservation_block = 0;
}
}
if (f_structure_block) {
if (m__io__raw_structure_block) {
delete m__io__raw_structure_block; m__io__raw_structure_block = 0;
}
if (m_structure_block) {
delete m_structure_block; m_structure_block = 0;
}
}
if (f_strings_block) {
if (m__io__raw_strings_block) {
delete m__io__raw_strings_block; m__io__raw_strings_block = 0;
}
if (m_strings_block) {
delete m_strings_block; m_strings_block = 0;
}
}
}
dtb_t::memory_block_t::memory_block_t(kaitai::kstream* p__io, dtb_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_entries = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::memory_block_t::_read() {
m_entries = new std::vector<memory_block_entry_t*>();
{
int i = 0;
while (!m__io->is_eof()) {
m_entries->push_back(new memory_block_entry_t(m__io, this, m__root));
i++;
}
}
}
dtb_t::memory_block_t::~memory_block_t() {
_clean_up();
}
void dtb_t::memory_block_t::_clean_up() {
if (m_entries) {
for (std::vector<memory_block_entry_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
delete *it;
}
delete m_entries; m_entries = 0;
}
}
dtb_t::fdt_block_t::fdt_block_t(kaitai::kstream* p__io, dtb_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_nodes = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::fdt_block_t::_read() {
m_nodes = new std::vector<fdt_node_t*>();
{
int i = 0;
fdt_node_t* _;
do {
_ = new fdt_node_t(m__io, this, m__root);
m_nodes->push_back(_);
i++;
} while (!(_->type() == dtb_t::FDT_END));
}
}
dtb_t::fdt_block_t::~fdt_block_t() {
_clean_up();
}
void dtb_t::fdt_block_t::_clean_up() {
if (m_nodes) {
for (std::vector<fdt_node_t*>::iterator it = m_nodes->begin(); it != m_nodes->end(); ++it) {
delete *it;
}
delete m_nodes; m_nodes = 0;
}
}
dtb_t::memory_block_entry_t::memory_block_entry_t(kaitai::kstream* p__io, dtb_t::memory_block_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::memory_block_entry_t::_read() {
m_address = m__io->read_u8be();
m_size = m__io->read_u8be();
}
dtb_t::memory_block_entry_t::~memory_block_entry_t() {
_clean_up();
}
void dtb_t::memory_block_entry_t::_clean_up() {
}
dtb_t::strings_t::strings_t(kaitai::kstream* p__io, dtb_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_strings = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::strings_t::_read() {
m_strings = new std::vector<std::string>();
{
int i = 0;
while (!m__io->is_eof()) {
m_strings->push_back(kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII")));
i++;
}
}
}
dtb_t::strings_t::~strings_t() {
_clean_up();
}
void dtb_t::strings_t::_clean_up() {
if (m_strings) {
delete m_strings; m_strings = 0;
}
}
dtb_t::fdt_prop_t::fdt_prop_t(kaitai::kstream* p__io, dtb_t::fdt_node_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_name = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::fdt_prop_t::_read() {
m_len_property = m__io->read_u4be();
m_ofs_name = m__io->read_u4be();
m_property = m__io->read_bytes(len_property());
m_padding = m__io->read_bytes(kaitai::kstream::mod(-(_io()->pos()), 4));
}
dtb_t::fdt_prop_t::~fdt_prop_t() {
_clean_up();
}
void dtb_t::fdt_prop_t::_clean_up() {
if (f_name) {
}
}
std::string dtb_t::fdt_prop_t::name() {
if (f_name)
return m_name;
kaitai::kstream *io = _root()->strings_block()->_io();
std::streampos _pos = io->pos();
io->seek(ofs_name());
m_name = kaitai::kstream::bytes_to_str(io->read_bytes_term(0, false, true, true), std::string("ASCII"));
io->seek(_pos);
f_name = true;
return m_name;
}
dtb_t::fdt_node_t::fdt_node_t(kaitai::kstream* p__io, dtb_t::fdt_block_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::fdt_node_t::_read() {
m_type = static_cast<dtb_t::fdt_t>(m__io->read_u4be());
n_body = true;
switch (type()) {
case dtb_t::FDT_BEGIN_NODE: {
n_body = false;
m_body = new fdt_begin_node_t(m__io, this, m__root);
break;
}
case dtb_t::FDT_PROP: {
n_body = false;
m_body = new fdt_prop_t(m__io, this, m__root);
break;
}
}
}
dtb_t::fdt_node_t::~fdt_node_t() {
_clean_up();
}
void dtb_t::fdt_node_t::_clean_up() {
if (!n_body) {
if (m_body) {
delete m_body; m_body = 0;
}
}
}
dtb_t::fdt_begin_node_t::fdt_begin_node_t(kaitai::kstream* p__io, dtb_t::fdt_node_t* p__parent, dtb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void dtb_t::fdt_begin_node_t::_read() {
m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII"));
m_padding = m__io->read_bytes(kaitai::kstream::mod(-(_io()->pos()), 4));
}
dtb_t::fdt_begin_node_t::~fdt_begin_node_t() {
_clean_up();
}
void dtb_t::fdt_begin_node_t::_clean_up() {
}
dtb_t::memory_block_t* dtb_t::memory_reservation_block() {
if (f_memory_reservation_block)
return m_memory_reservation_block;
std::streampos _pos = m__io->pos();
m__io->seek(ofs_memory_reservation_block());
m__raw_memory_reservation_block = m__io->read_bytes((ofs_structure_block() - ofs_memory_reservation_block()));
m__io__raw_memory_reservation_block = new kaitai::kstream(m__raw_memory_reservation_block);
m_memory_reservation_block = new memory_block_t(m__io__raw_memory_reservation_block, this, m__root);
m__io->seek(_pos);
f_memory_reservation_block = true;
return m_memory_reservation_block;
}
dtb_t::fdt_block_t* dtb_t::structure_block() {
if (f_structure_block)
return m_structure_block;
std::streampos _pos = m__io->pos();
m__io->seek(ofs_structure_block());
m__raw_structure_block = m__io->read_bytes(len_structure_block());
m__io__raw_structure_block = new kaitai::kstream(m__raw_structure_block);
m_structure_block = new fdt_block_t(m__io__raw_structure_block, this, m__root);
m__io->seek(_pos);
f_structure_block = true;
return m_structure_block;
}
dtb_t::strings_t* dtb_t::strings_block() {
if (f_strings_block)
return m_strings_block;
std::streampos _pos = m__io->pos();
m__io->seek(ofs_strings_block());
m__raw_strings_block = m__io->read_bytes(len_strings_block());
m__io__raw_strings_block = new kaitai::kstream(m__raw_strings_block);
m_strings_block = new strings_t(m__io__raw_strings_block, this, m__root);
m__io->seek(_pos);
f_strings_block = true;
return m_strings_block;
}