[PATCH v2 4/8] binman: Add QCDT support

Simon Glass sjg at chromium.org
Mon Jun 8 15:27:01 CEST 2026


Hi Sam,

On 2026-06-07T23:37:09, Sam Day via B4 Relay
<devnull+me.samcday.com at kernel.org> wrote:
> binman: Add QCDT support
>
> This vendor-specific format is used by many bootloaders on older qcom
> SoCs, such as msm8916. It's a container for N FDTs. Each one is
> contained in a record that includes metadata about the platform/variant
> it targets. The previous bootloader picks the 'right' record based on
> this metadata.
>
> This initial impl targets a streamlined v2 path, with no support for
> different versions or multiple qcom,msm-id/qcom,board-id tuples. If/when
> that's needed it will be implemented in a follow-up.
>
> Signed-off-by: Sam Day <me at samcday.com>
>
> tools/binman/etype/android_boot.py           |  30 +++++
>  tools/binman/etype/qcdt.py                   | 160 +++++++++++++++++++++++++++
>  tools/binman/ftest.py                        |  22 ++++
>  tools/binman/test/vendor/qcdt.dts            |  20 ++++
>  tools/binman/test/vendor/qcdt_bad_msm_id.dts |  17 +++
>  5 files changed, 249 insertions(+)

Your comment says "A legacy QCDT abootimg, the kind msm8916 bootloaders expect"

Please can you spell out the acronym...I would also prefer qcom_xxx
for the etype name where xxx is whatever this means, since presumably
this is Qualcomm-specific.

> diff --git a/tools/binman/etype/qcdt.py b/tools/binman/etype/qcdt.py
> @@ -0,0 +1,160 @@
> +from binman.entry import Entry
> +from binman.etype.section import Entry_section
> +from dtoc import fdt_util

The _align_up()/_pad() helpers at the top are duplicated from
android_boot.py in patch 1, and will be again in dtbh.py in patch 6.
Please factor them into a shared helper module (or stick them on
Entry_section).

> diff --git a/tools/binman/etype/qcdt.py b/tools/binman/etype/qcdt.py
> @@ -0,0 +1,160 @@
> +    def ReadEntries(self):
> +        for node in self._node.subnodes:
> +            if self.IsSpecialSubnode(node):
> +                continue
> +
> +            payloads = self._GetPayloadSubnodes(node)
> +            if len(payloads) > 1:
> +                raise ValueError("Node '%s': must contain exactly one DTB "
> +                                 "payload subnode" % node.path)

Please use self.Raise() throughout - that's the binman convention and
it adds the node path for you. The static/classmethod helpers should
become plain methods so they can use self.Raise() too.

> diff --git a/tools/binman/etype/qcdt.py b/tools/binman/etype/qcdt.py
> @@ -0,0 +1,160 @@
> +        dtb_offset = _align_up(12 + len(dtbs) * 24, page_size)
> +        records = []
> +        payloads = bytearray()
> +        for msm_id, board_id, dtb in dtbs:
> +            dtb_size = _align_up(len(dtb), page_size)
> +            records.append((*msm_id, *board_id, dtb_offset, dtb_size))
> +            payloads += _pad(dtb, page_size)
> +            dtb_offset += dtb_size
> +
> +        qcdt = bytearray(struct.pack('<4sII', QCDT_MAGIC, QCDT_VERSION,
> +                                     len(records)))
> +        for platform_id, soc_rev, variant_id, subtype, offset, size in records:
> +            qcdt += struct.pack('<IIIIII', platform_id, variant_id, subtype,
> +                                soc_rev, offset, size)

The 12 and 24 magic numbers should be struct.calcsize() of named
header/record format strings, the way android_boot.py does for
BOOT_IMAGE_HEADER_V0_SIZE. That also documents the layout.

Also, unpacking msm-id as (platform_id, soc_rev) and board-id as
(variant_id, subtype), then packing in the swapped order (platform_id,
variant_id, subtype, soc_rev) is hard to read. Please build each
record directly with the on-disk field order and add a comment
referencing the LK/AOSP source for the QCDT v2 record layout.

> diff --git a/tools/binman/etype/qcdt.py b/tools/binman/etype/qcdt.py
> @@ -0,0 +1,160 @@
> +    def BuildSectionData(self, required):
> +        if not self._node.subnodes:
> +            raise ValueError("Node '%s': Missing required DTB subnodes" %
> +                             self._node.path)

Redundant with the 'if not dtbs:' check further down (which correctly
handles the case where the only subnodes are special). Please drop it.

> diff --git a/tools/binman/etype/qcdt.py b/tools/binman/etype/qcdt.py
> @@ -0,0 +1,160 @@
> +    def _GetPageSize(self):
> +        if self._page_size is not None:
> +            return self._page_size
> +
> +        return getattr(self.section, 'page_size', 2048)

The docstring says "defaults to the parent android-boot page size",
but in practice this picks it up from the vendor-dt section that
_ReadVendorDtEntries() stamps with a page_size attribute. Please
update the docstring - or better, but really you should look up the
enclosing android-boot section directly rather than relying on an
attribute being monkey-patched onto an intermediate generic section.

> diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
> @@ -5673,6 +5673,28 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
> +    def testAndroidBootQcdt(self):
> +        """Test that binman can produce a QCDT container"""
> +        data, dtb_data, _map, _dtb = self._DoReadFileDtb(
> +            'vendor/qcdt.dts', use_real_dtb=True)

Only one record is covered. Since the whole point of QCDT is to hold
multiple DTBs picked by msm-id/board-id, please add a test with at
least two dtb-* records so the offset/size table-walking code is
actually exercised.

> +    binman: Add QCDT support
> +
> +    This initial impl targets a streamlined v2 path, with no support for
> +    different versions or multiple qcom,msm-id/qcom,board-id tuples. If/when
> +    that's needed it will be implemented in a follow-up.

How abouta Link: to the LK/AOSP reference for the QCDT v2 record format?

Regards,
Simon


More information about the U-Boot mailing list