[PATCH v3 06/10] binman: android_boot: vendor-dt support
Sam Day via B4 Relay
devnull+me.samcday.com at kernel.org
Wed Jun 10 03:27:44 CEST 2026
From: Sam Day <me at samcday.com>
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 | 41 ++++++++++++++++++++++++++--
tools/binman/ftest.py | 15 ++++++++++
tools/binman/test/android_boot_vendor_dt.dts | 27 ++++++++++++++++++
3 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/tools/binman/etype/android_boot.py b/tools/binman/etype/android_boot.py
index 8b72d90acc5..5cfa71ee981 100644
--- a/tools/binman/etype/android_boot.py
+++ b/tools/binman/etype/android_boot.py
@@ -33,6 +33,9 @@ class Entry_android_boot(Entry_section):
A kernel payload, optional ramdisk payload can be supplied. A DTB payload
can also be provided when header_version == v2.
+ Vendor-specific payloads are also supported. These are non-standard
+ v0 images with a special DT container format appended.
+
Properties / Entry arguments:
- header-version: Android boot image header version, must be 0 or 2,
defaults to 0
@@ -50,6 +53,7 @@ class Entry_android_boot(Entry_section):
- kernel: section containing the executable payload
- dtb: section containing the DTB payload, used by header version 2 only
- ramdisk: optional section containing a ramdisk payload
+ - vendor-dt: legacy vendor DT payload, used by header version 0 only
Example::
A v2 abootimg with control FDT placed in the DTB section:
@@ -112,6 +116,7 @@ class Entry_android_boot(Entry_section):
self.os_version = fdt_util.GetInt(self._node, 'os-version', 0)
self.boot_name = fdt_util.GetString(self._node, 'boot-name', '')
self.cmdline = fdt_util.GetString(self._node, 'cmdline', '')
+ self.vendor_dt_node = self._node.FindNode('vendor-dt')
if self.header_version not in (0, 2):
self.Raise('Only Android boot image header versions 0 and 2 are '
@@ -132,9 +137,15 @@ class Entry_android_boot(Entry_section):
self.Raise('page-size must fit the Android boot image header')
if 'dtb' not in self._entries:
self.Raise("Missing required subnode 'dtb'")
+ if self.vendor_dt_node:
+ self.Raise("Subnode 'vendor-dt' requires header-version 0")
def ReadEntries(self):
for node in self._node.subnodes:
+ if node.name == 'vendor-dt':
+ self._ReadVendorDtEntries(node)
+ continue
+
if node.name not in ('kernel', 'ramdisk', 'dtb'):
self.Raise("Unexpected subnode '%s'" % node.name)
@@ -145,6 +156,14 @@ 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.ReadNode()
+ entry.SetPrefix(self._name_prefix)
+ self._entries[vendor_dt_node.name] = entry
+
def _GetIntCells(self, propname, default):
prop = self._node.props.get(propname)
if not prop:
@@ -198,8 +217,14 @@ class Entry_android_boot(Entry_section):
return default
return entry.GetData(required)
+ def _BuildVendorDt(self, required):
+ if not self.vendor_dt_node:
+ return b''
+ return self._GetEntryData('vendor-dt', required)
+
def _BuildV0SectionData(self, required):
kernel = self._GetEntryData('kernel', required)
+ vendor_dt = self._BuildVendorDt(required)
ramdisk = self._GetEntryData('ramdisk', required, b'')
if not required and (kernel is None or vendor_dt is None or
ramdisk is None):
@@ -211,8 +236,18 @@ class Entry_android_boot(Entry_section):
BOOT_ARGS_SIZE)
boot_id_payloads = [kernel, ramdisk, b'']
+ if self.vendor_dt_node:
+ boot_id_payloads.append(vendor_dt)
image_id = self._BootId(*boot_id_payloads)
+ overloaded_header_version = self.header_version
+ if self.vendor_dt_node:
+ # vendor DTs overload the header_version field to store the length
+ # of the appended payload. Hopefully AOSP abootimg never progresses
+ # to v8192-ish or we might have some real specificity problems on
+ # our hands.
+ overloaded_header_version = len(vendor_dt)
+
header = struct.pack(BOOT_IMAGE_HEADER_V0,
BOOT_MAGIC,
len(kernel),
@@ -223,7 +258,7 @@ class Entry_android_boot(Entry_section):
0, # second_offset
self._GetAddr(self.tags_offset, 'tags'),
self.page_size,
- self.header_version,
+ overloaded_header_version,
self.os_version,
boot_name,
cmdline,
@@ -233,6 +268,7 @@ class Entry_android_boot(Entry_section):
image += self.PadToAlignment(header, self.page_size)
image += self.PadToAlignment(kernel, self.page_size)
image += self.PadToAlignment(ramdisk, self.page_size)
+ image += self.PadToAlignment(vendor_dt, self.page_size)
return bytes(image)
@@ -240,7 +276,8 @@ class Entry_android_boot(Entry_section):
kernel = self._GetEntryData('kernel', required)
dtb = self._GetEntryData('dtb', required)
ramdisk = self._GetEntryData('ramdisk', required, b'')
- if not required and (kernel is None or dtb is None):
+ if not required and (kernel is None or dtb is None or
+ ramdisk is None):
return None
boot_name = self._CheckFit('boot-name', self.boot_name.encode('ascii'),
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 71740205c72..bbdcb721eca 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -5746,6 +5746,21 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertIn("Subnode 'vendor-dt' requires header-version 0",
str(exc.exception))
+ def testAndroidBootVendorDt(self):
+ """Test that android-boot can embed an arbitrary vendor-dt section"""
+ data = self._DoReadFile('android_boot_vendor_dt.dts')
+ header = struct.unpack_from('<8s10I16s512s32s', data, 0)
+ page_size = 2048
+ vendor_dt_offset = page_size * 3
+ 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 + b'\0' * (page_size - len(vendor_dt)),
+ data[vendor_dt_offset:vendor_dt_offset + page_size])
+ self.assertEqual(vendor_dt_offset + page_size, len(data))
+
def testFitFdtOper(self):
"""Check handling of a specified FIT operation"""
entry_args = {
diff --git a/tools/binman/test/android_boot_vendor_dt.dts b/tools/binman/test/android_boot_vendor_dt.dts
new file mode 100644
index 00000000000..194396a0880
--- /dev/null
+++ b/tools/binman/test/android_boot_vendor_dt.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ binman {
+ android-boot {
+ header-version = <0>;
+ kernel {
+ u-boot {
+ no-expanded;
+ };
+ };
+ ramdisk {
+ fill {
+ size = <1>;
+ };
+ };
+ vendor-dt {
+ text {
+ size = <5>;
+ text = "howdy";
+ };
+ };
+ };
+ };
+};
--
2.54.0
More information about the U-Boot
mailing list