From: Sam Day <[email protected]> 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 <[email protected]> --- 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

