[PATCH v2 3/8] binman: android_boot: vendor-dt support

Sam Day me at samcday.com
Tue Jun 9 10:34:54 CEST 2026


'Ello Simon,

On Monday, 8 June 2026 at 11:27 PM, Simon Glass <sjg at chromium.org> wrote:

> Hi Sam,
> 
> On 2026-06-07T23:37:09, Sam Day via B4 Relay
> <devnull+me.samcday.com at kernel.org> wrote:
> > binman: android_boot: vendor-dt support
> >
> > There's many Android bootloaders out there that expect non-standard
> > abootimgs. Samsung and Qualcomm, and likely many others. This quirk has
> > been codified in the widely used osm0sis fork/rewrite of mkbootimg.
> >
> > Presumably, these vendor-specific abootimgs were conceived before the v2
> > format was widely available or specified. The evidence for this is their
> > hijacking of the header_version field to specify the length of a payload
> > that follows the main abootimg.
> >
> > At least QCDT and DTBH (both of which will be implemented as binman
> > etypes in following commits) use this approach.
> >
> > Link: https://github.com/osm0sis/mkbootimg
> > Signed-off-by: Sam Day <me at samcday.com>
> >
> > tools/binman/etype/android_boot.py                 | 37 ++++++++++++++++++++--
> >  tools/binman/ftest.py                              | 11 ++++++-
> >  .../binman/test/vendor/android_boot_vendor_dt.dts  | 27 ++++++++++++++++
> >  3 files changed, 72 insertions(+), 3 deletions(-)
> 
> > diff --git a/tools/binman/etype/android_boot.py b/tools/binman/etype/android_boot.py
> > @@ -131,17 +136,26 @@ class Entry_android_boot(Entry_section):
> >                  self.Raise('page-size must fit the Android boot image header')
> >              if 'dtb' in self._entries:
> >                  self.Raise("Subnode 'dtb' requires header-version 2")
> > +            if self.vendor_dt_node and self.os_version:
> > +                self.Raise("os_version not allowed when vendor-dt is in use")
> 
> Two things. The message uses the Python attribute name; users see the
> DT property, so this should read 'os-version'. Second, the commit
> message doesn't explain why os-version becomes invalid in this mode -
> in the v0 header layout, dt_size (the field we hijack) and os_version
> are separate u32 fields, so there's no obvious conflict.

Good point. I double checked and there's no need to add / enforce this
invariant. I simply added it purely out of paranoia, since it's right next
to the header_version and possibly some bootloader somewhere interprets
the "vendor DT" size as 64 bit. But then again, probably not ;)

> 
> If a downstream bootloader interprets os_version differently when
> dt_size is set, please document that - otherwise I'd drop the
> restriction. Either way, please add a test for the Raise().

mkbootimg-osm0sis doesn't enforce it, at any rate. So I've removed
the check from v3.

> 
> > diff --git a/tools/binman/etype/android_boot.py b/tools/binman/etype/android_boot.py
> > @@ -152,6 +166,15 @@ class Entry_android_boot(Entry_section):
> >              entry.SetPrefix(self._name_prefix)
> >              self._entries[node.name] = entry
> >
> > +    def _ReadVendorDtEntries(self, vendor_dt_node):
> > +        entry = Entry.Create(self, vendor_dt_node, etype='section',
> > +                             expanded=self.GetImage().use_expanded,
> > +                             missing_etype=self.GetImage().missing_etype)
> > +        entry.page_size = fdt_util.GetInt(self._node, 'page-size', 2048)
> > +        entry.ReadNode()
> > +        entry.SetPrefix(self._name_prefix)
> > +        self._entries[vendor_dt_node.name] = entry
> 
> Forcing etype='section' here works only because qcdt/dtbh later walks
> up via getattr(self.section, 'page_size', 2048). That coupling is
> invisible from this file. Please add a comment noting this attribute
> is consumed by child etypes (qcdt/dtbh in later patches) - otherwise
> it looks like dead code. A cleaner alternative would be for the child
> etype to look up the android-boot ancestor directly, so page-size
> doesn't need to be smuggled through an unrelated attribute.
> 
> Also 'page-size' is re-read from self._node here, but ReadNode() has
> already cached it in self.page_size. Just use that.

I opted for the cleaner alternative you proposed. In v3 the qcdt/dtbh
etypes traverse their ancestors to find an explicit page-size, falling
back to the local prop, or 2048 otherwise.

> 
> > diff --git a/tools/binman/etype/android_boot.py b/tools/binman/etype/android_boot.py
> > @@ -246,7 +278,7 @@ class Entry_android_boot(Entry_section):
> >                               0, # second_offset
> >                               self._GetAddr(self.tags_offset, 'tags'),
> >                               self.page_size,
> > -                             self.header_version,
> > +                             len(vendor_dt),
> 
> This conflates two distinct fields. For a plain v0 image the slot is
> header_version; for a vendor-dt image it's dt_size. The current code
> only produces the right bytes because both collapse to 0 when there is
> no vendor-dt. Please make it explicit:
> 
>     dt_size = len(vendor_dt) if self.vendor_dt_node else self.header_version
>

I did actually code-golf it that way, but I agree it's too clever ;)
(And by clever I mean obnoxiously obtuse)

I've introduced an explicit `overloaded_header_version` in v3, and
included a comment about this special/weird behaviour.

> and add a comment that this slot is overloaded. As written, anyone
> adding a new v0 header_version in future will silently break vendor-dt
> builds.

Not sure I follow this part. _BuildV0SectionData is only ever called when
self.header_version == 0.

> 
> > diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
> > @@ -5662,7 +5662,16 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
> >          self.assertEqual(U_BOOT_DTB_DATA,
> >                           data[0x1000:0x1000 + len(U_BOOT_DTB_DATA)])
> >
> > -        self.assertEqual(U_BOOT_DATA, data[0x800:0x800 + len(U_BOOT_DATA)])
> > +    def testAndroidBootVendorDt(self):
> 
> You're silently dropping a duplicated assertion left over from patch
> 1/8. That cleanup belongs in its own patch (or a fixup to 1/8), not
> buried in the vendor-dt commit.

Aye, oops. That's cleaned up properly in v3.

> 
> > diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
> > @@ -5662,7 +5662,16 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
> > +    def testAndroidBootVendorDt(self):
> > +        """Test that android-boot can embed an arbitrary vendor-dt section"""
> > +        data = self._DoReadFile('vendor/android_boot_vendor_dt.dts')
> > +        header = struct.unpack_from('<8s10I16s512s32s', data, 0)
> > +        vendor_dt = b'howdy'
> > +        self.assertEqual(len(vendor_dt), header[9])
> > +        self.assertEqual(0, header[10])
> > +        self.assertEqual(self._AndroidBootId(U_BOOT_DATA, b'\0', b'',
> > +                                             vendor_dt), header[13])
> > +        self.assertEqual(vendor_dt, data[0x1800:0x1805])
> 
> Coverage is thin. Please add tests for the two new Raise() paths
> ('vendor-dt' on v2, os-version with vendor-dt), and assert that
> vendor_dt is padded to page-size - slicing [0x1800:0x1805] only proves
> the first five bytes landed, not that the next 0x7fb bytes are zero or
> that the overall image length is right.

Ack - v3 will have 100% test coverage for all 3 newly introduced etypes.

Again, thanks for the very comprehensive review!

Kind regards,
-Sam

> 
> Regards,
> Simon
> 


More information about the U-Boot mailing list