Android DTB/DTBO Partition: Python (read-write) parsing library

Format for Android DTB/DTBO partitions. It's kind of archive with dtb/dtbo files. Used only when there is a separate unique partition (dtb, dtbo) on an android device to organize device tree files. The format consists of a header with info about size and number of device tree entries and the entries themselves. This format description could be used to extract device tree entries from a partition images and decompile them with dtc (device tree compiler).

File extension

img

KS implementation details

License: CC0-1.0

This page hosts a formal specification of Android DTB/DTBO Partition using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Python (read-write) source code to parse Android DTB/DTBO Partition

android_dto.py

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
# type: ignore

import kaitaistruct
from kaitaistruct import ReadWriteKaitaiStruct, KaitaiStream, BytesIO


if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 11):
    raise Exception("Incompatible Kaitai Struct Python API: 0.11 or later is required, but you have %s" % (kaitaistruct.__version__))

class AndroidDto(ReadWriteKaitaiStruct):
    """Format for Android DTB/DTBO partitions. It's kind of archive with
    dtb/dtbo files. Used only when there is a separate unique partition
    (dtb, dtbo) on an android device to organize device tree files.
    The format consists of a header with info about size and number
    of device tree entries and the entries themselves. This format
    description could be used to extract device tree entries from a
    partition images and decompile them with dtc (device tree compiler).
    
    .. seealso::
       Source - https://source.android.com/docs/core/architecture/dto/partitions
    
    
    .. seealso::
       Source - https://android.googlesource.com/platform/system/libufdt/+/refs/tags/android-10.0.0_r47
    """
    def __init__(self, _io=None, _parent=None, _root=None):
        super(AndroidDto, self).__init__(_io)
        self._parent = _parent
        self._root = _root or self

    def _read(self):
        self.header = AndroidDto.DtTableHeader(self._io, self, self._root)
        self.header._read()
        self.entries = []
        for i in range(self.header.dt_entry_count):
            _t_entries = AndroidDto.DtTableEntry(self._io, self, self._root)
            try:
                _t_entries._read()
            finally:
                self.entries.append(_t_entries)

        self._dirty = False


    def _fetch_instances(self):
        pass
        self.header._fetch_instances()
        for i in range(len(self.entries)):
            pass
            self.entries[i]._fetch_instances()



    def _write__seq(self, io=None):
        super(AndroidDto, self)._write__seq(io)
        self.header._write__seq(self._io)
        for i in range(len(self.entries)):
            pass
            self.entries[i]._write__seq(self._io)



    def _check(self):
        if self.header._root != self._root:
            raise kaitaistruct.ConsistencyError(u"header", self._root, self.header._root)
        if self.header._parent != self:
            raise kaitaistruct.ConsistencyError(u"header", self, self.header._parent)
        if len(self.entries) != self.header.dt_entry_count:
            raise kaitaistruct.ConsistencyError(u"entries", self.header.dt_entry_count, len(self.entries))
        for i in range(len(self.entries)):
            pass
            if self.entries[i]._root != self._root:
                raise kaitaistruct.ConsistencyError(u"entries", self._root, self.entries[i]._root)
            if self.entries[i]._parent != self:
                raise kaitaistruct.ConsistencyError(u"entries", self, self.entries[i]._parent)

        self._dirty = False

    class DtTableEntry(ReadWriteKaitaiStruct):
        def __init__(self, _io=None, _parent=None, _root=None):
            super(AndroidDto.DtTableEntry, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._should_write_body = False
            self.body__enabled = True

        def _read(self):
            self.dt_size = self._io.read_u4be()
            self.dt_offset = self._io.read_u4be()
            self.id = self._io.read_u4be()
            self.rev = self._io.read_u4be()
            self.custom = []
            for i in range(4):
                self.custom.append(self._io.read_u4be())

            self._dirty = False


        def _fetch_instances(self):
            pass
            for i in range(len(self.custom)):
                pass

            _ = self.body
            if hasattr(self, '_m_body'):
                pass



        def _write__seq(self, io=None):
            super(AndroidDto.DtTableEntry, self)._write__seq(io)
            self._should_write_body = self.body__enabled
            self._io.write_u4be(self.dt_size)
            self._io.write_u4be(self.dt_offset)
            self._io.write_u4be(self.id)
            self._io.write_u4be(self.rev)
            for i in range(len(self.custom)):
                pass
                self._io.write_u4be(self.custom[i])



        def _check(self):
            if len(self.custom) != 4:
                raise kaitaistruct.ConsistencyError(u"custom", 4, len(self.custom))
            for i in range(len(self.custom)):
                pass

            if self.body__enabled:
                pass
                if len(self._m_body) != self.dt_size:
                    raise kaitaistruct.ConsistencyError(u"body", self.dt_size, len(self._m_body))

            self._dirty = False

        @property
        def body(self):
            """DTB/DTBO file."""
            if self._should_write_body:
                self._write_body()
            if hasattr(self, '_m_body'):
                return self._m_body

            if not self.body__enabled:
                return None

            io = self._root._io
            _pos = io.pos()
            io.seek(self.dt_offset)
            self._m_body = io.read_bytes(self.dt_size)
            io.seek(_pos)
            return getattr(self, '_m_body', None)

        @body.setter
        def body(self, v):
            self._dirty = True
            self._m_body = v

        def _write_body(self):
            self._should_write_body = False
            io = self._root._io
            _pos = io.pos()
            io.seek(self.dt_offset)
            io.write_bytes(self._m_body)
            io.seek(_pos)


    class DtTableHeader(ReadWriteKaitaiStruct):
        def __init__(self, _io=None, _parent=None, _root=None):
            super(AndroidDto.DtTableHeader, self).__init__(_io)
            self._parent = _parent
            self._root = _root

        def _read(self):
            self.magic = self._io.read_bytes(4)
            if not self.magic == b"\xD7\xB7\xAB\x1E":
                raise kaitaistruct.ValidationNotEqualError(b"\xD7\xB7\xAB\x1E", self.magic, self._io, u"/types/dt_table_header/seq/0")
            self.total_size = self._io.read_u4be()
            self.header_size = self._io.read_u4be()
            self.dt_entry_size = self._io.read_u4be()
            self.dt_entry_count = self._io.read_u4be()
            self.dt_entries_offset = self._io.read_u4be()
            self.page_size = self._io.read_u4be()
            self.version = self._io.read_u4be()
            self._dirty = False


        def _fetch_instances(self):
            pass


        def _write__seq(self, io=None):
            super(AndroidDto.DtTableHeader, self)._write__seq(io)
            self._io.write_bytes(self.magic)
            self._io.write_u4be(self.total_size)
            self._io.write_u4be(self.header_size)
            self._io.write_u4be(self.dt_entry_size)
            self._io.write_u4be(self.dt_entry_count)
            self._io.write_u4be(self.dt_entries_offset)
            self._io.write_u4be(self.page_size)
            self._io.write_u4be(self.version)


        def _check(self):
            if len(self.magic) != 4:
                raise kaitaistruct.ConsistencyError(u"magic", 4, len(self.magic))
            if not self.magic == b"\xD7\xB7\xAB\x1E":
                raise kaitaistruct.ValidationNotEqualError(b"\xD7\xB7\xAB\x1E", self.magic, None, u"/types/dt_table_header/seq/0")
            self._dirty = False