Flattened Devicetree Format: C++98/STL parsing library

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:

Application

["Linux", "Das U-Boot"]

File extension

dtb

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.9

References

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.

Usage

Runtime 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.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (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);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    dtb_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic() // => get magic

C++98/STL source code to parse Flattened Devicetree Format

dtb.h

#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_

dtb.cpp

// 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;
}