* [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images
@ 2026-05-11 13:57 Casey Connolly
2026-05-11 13:57 ` [PATCH v7 1/6] config.mk: support vendor generic includes Casey Connolly
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
Currently the build process for flashing U-Boot as the first-stage
bootloader on Qualcomm platforms requires a manual step of test-signing
the emitted ELF binary with the appropriate settings for it to be
properly accepted by the pre-loader.
To address this, let's introduce a new tool "mkmbn.py", this is based
on the existing qtestsign[1] tool but introduces lookup table that maps
from a boards DT compatible property to the load address it needs.
A new config option (QCOM_GENERATE_MBN) is added so that
BUILD_TARGET can be set automatically and avoid the need to manually
specify the target on the cmdline. The make target is added in
board/qualcomm/config.mk with a minor adjustment to the toplevel
config.mk to properly check this path.
The various platforms which currently rely on manual post-processing
with qtestsign are adapted to leverage mkmbn instead. New platforms can
be supported just by adding an entry to mkmbn.py with the same data that
would otherwise be encoded in the qtestsign cmdline args.
The Qualcomm documentation is also cleaned up, a new "signing" page
is added to briefly cover the what and why of MBN signing, and board
specific pages are updated to explain the new build process.
[1]: https://github.com/msm8916-mainline/qtestsign
---
Changes in v7:
- Adjust config.mk include to include both generic vendor config.mk
path and board-specific path so that both can be used simultaneously.
- Also enable mkmbn for db410c
- Rename db410c/820c defconfigs to have qcom_ prefix
- Link to v6: https://patch.msgid.link/20260505-b4-qcom-tooling-improvements-v6-0-a5fb673f4af6@linaro.org
Changes in v6:
- Drop binman in favour of introducing a make target and associated
kconfig infra, this drastically simplifies the tooling.
- Various minor fixes/improvements to mkmbn.py
- Link to v5: https://lore.kernel.org/r/20250722-b4-qcom-tooling-improvements-v5-0-df143f1247fc@linaro.org
Changes in v5:
- Rebase on master (dropped trogdor chromebook series)
- Directly import the MBN signing code from qtestsign and then adjust
it in a separate commit so it can be updated in the future.
- Disable BINMAN_FDT when ARCH_SNAPDRAGON is enabled
- Set default value for BINMAN_DTB in mach-snapdragon/Kconfig rather
than having to set it in all defconfigs.
- Add a comment to the top of mkmbn.py explaining it's purpose.
- Link to v4: https://lore.kernel.org/r/20250613-b4-qcom-tooling-improvements-v4-0-7ea1c68779fa@linaro.org
Changes in v4:
- Rework how qtestsign code is imported to make it easier to sync with upstream in the future.
- Actually raise an exception when mkmbn fails in an unexpected way.
- Link to v3: https://lore.kernel.org/r/20250612-b4-qcom-tooling-improvements-v3-0-76f34cf216e2@linaro.org
Changes in v3:
- Fixup Makefiles and add missing qcom-binman.dts
- Adjust wording to reflect changes to build process
- Link to v2: https://lore.kernel.org/r/20250602-b4-qcom-tooling-improvements-v2-0-c7d19c0d4a8b@linaro.org
Changes in v2:
- Reworked to use binman with a plugin to build the u-boot.mbn file
- Added some fixes for binman to work with OF_UPSTREAM and with tools
in the srctree toolpath rather than objtree for out of tree builds.
- Link to v1: https://lore.kernel.org/r/20250522-b4-qcom-tooling-improvements-v1-0-8141b8955cfb@linaro.org
To: u-boot@lists.denx.de
To: Sumit Garg <sumit.garg@kernel.org>
To: u-boot-qcom@groups.io
Cc: Tom Rini <trini@konsulko.com>
Cc: Casey Connolly <casey.connolly@linaro.org>
Cc: Neil Armstrong <neil.armstrong@linaro.org>
Cc: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
Cc: Varadarajan Narayanan <quic_varada@quicinc.com>
Cc: Quentin Schulz <quentin.schulz@cherry.de>
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
Cc: Marek Vasut <marek.vasut+renesas@mailbox.org>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Cc: Michal Simek <michal.simek@amd.com>
Cc: David Lechner <dlechner@baylibre.com>
---
Casey Connolly (6):
config.mk: support vendor generic includes
tools: qcom: introduce mkmbn library
tools: qcom: add mkmbn.py
doc: board/qualcomm: update docs for new u-boot.mbn target
mach-snapdragon: add kconfig infra for building MBN files
configs: qcom: use mkmbn and stop building ELF files
Kconfig | 1 +
arch/arm/mach-snapdragon/Kconfig | 11 +
board/qualcomm/config.mk | 14 +
config.mk | 8 +-
configs/qcm6490_defconfig | 2 +-
...0c_defconfig => qcom_dragonboard410c_defconfig} | 2 +-
...0c_defconfig => qcom_dragonboard820c_defconfig} | 0
configs/qcom_ipq9574_mmc_defconfig | 2 +-
configs/qcom_qcs615_defconfig | 2 +-
configs/qcom_qcs8300_defconfig | 2 +-
configs/qcom_qcs9100_defconfig | 2 +-
doc/board/qualcomm/board.rst | 2 +-
doc/board/qualcomm/dragonboard410c.rst | 19 +-
doc/board/qualcomm/dragonwing.rst | 14 +-
doc/board/qualcomm/index.rst | 1 +
doc/board/qualcomm/rb3gen2.rst | 30 +-
doc/board/qualcomm/rdp.rst | 5 +-
doc/board/qualcomm/signing.rst | 29 ++
tools/mkmbn | 1 +
tools/qcom/mkmbn/cert.py | 127 ++++++++
tools/qcom/mkmbn/elf.py | 241 ++++++++++++++
tools/qcom/mkmbn/hashseg.py | 356 +++++++++++++++++++++
tools/qcom/mkmbn/mkmbn.py | 165 ++++++++++
23 files changed, 986 insertions(+), 50 deletions(-)
---
base-commit: bb0f3eebb3c196d9b6efbbd1e5aa9b16abbb9ad6
// Casey (she/they)
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v7 1/6] config.mk: support vendor generic includes
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
@ 2026-05-11 13:57 ` Casey Connolly
2026-06-08 14:27 ` Tom Rini
2026-05-11 13:57 ` [PATCH v7 2/6] tools: qcom: introduce mkmbn library Casey Connolly
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
Currently only board/vendor/$(BOARD)/config.mk is supported, add the
additional usecase of having a vendor generic config.mk for adding
functionality like platform-specific build targets.
Additionally, fix the ifdef to correctly check for $(BOARDDIR) rather
than $(BOARD) since that's what is actually used in the include path.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
config.mk | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/config.mk b/config.mk
index abed9cb65c6f..22c21c0d3b2d 100644
--- a/config.mk
+++ b/config.mk
@@ -46,17 +46,21 @@ sinclude $(srctree)/$(CPUDIR)/config.mk # include CPU specific rules
ifdef SOC
sinclude $(srctree)/$(CPUDIR)/$(SOC)/config.mk # include SoC specific rules
endif
ifneq ($(BOARD),)
-ifdef VENDOR
+ifneq ($(VENDOR),)
BOARDDIR = $(VENDOR)/$(BOARD)
ENVDIR=${vendor}/env
else
BOARDDIR = $(BOARD)
ENVDIR=${board}/env
endif
endif
-ifdef BOARD
+
+ifneq ($(VENDOR),)
+sinclude $(srctree)/board/$(VENDOR)/config.mk # include vendor specific rules
+endif
+ifdef BOARDDIR
sinclude $(srctree)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
ifdef FTRACE
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 2/6] tools: qcom: introduce mkmbn library
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
2026-05-11 13:57 ` [PATCH v7 1/6] config.mk: support vendor generic includes Casey Connolly
@ 2026-05-11 13:57 ` Casey Connolly
2026-05-11 13:57 ` [PATCH v7 3/6] tools: qcom: add mkmbn.py Casey Connolly
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
This is a fork of qtestsign[1] with modifications to integrate with the
U-Boot build system. It is pulled from
f3df53a5f0e3 ("Rename "fw" to "mbn"")
New Qualcomm dev boards flash U-Boot to the "uefi" partition, the format
is a standard ELF file with custom program headers containing Qualcomm
signatures, hashes and other metadata. Currently this is accomplished
with qtestsign manually, let's instead import it so we can integrate it
into the build process.
This library will be used by a new mkmbn.py tool to create MBN files
which can be directly flashed to the board.
[1]: https://github.com/msm8916-mainline/qtestsign
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
tools/qcom/mkmbn/cert.py | 127 ++++++++++++++++
tools/qcom/mkmbn/elf.py | 205 +++++++++++++++++++++++++
tools/qcom/mkmbn/hashseg.py | 356 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 688 insertions(+)
diff --git a/tools/qcom/mkmbn/cert.py b/tools/qcom/mkmbn/cert.py
new file mode 100644
index 000000000000..e14f88746d53
--- /dev/null
+++ b/tools/qcom/mkmbn/cert.py
@@ -0,0 +1,127 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2021-2022 Stephan Gerhold
+# See https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf
+# Somewhat based on code snippets from https://cryptography.io/en/latest/x509/tutorial.html
+from __future__ import annotations
+
+from datetime import datetime
+from typing import List
+
+from cryptography import x509
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.x509.oid import NameOID
+
+# NOTE: The certificate chain generated by qtestsign is NOT meant
+# to be secure. The private keys are listed here to make the
+# resulting files reproducible. THESE KEYS SHOULD ONLY BE USED
+# FOR TESTING AND NOT FOR A PROPER SECURE BOOT SETUP.
+
+ROOT_KEY = serialization.load_pem_private_key(b"""
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCjZqF/BwggY4Rs
+Q1/wSNPLEKQEROZ9i/d+7CXZCukWph+SKHlv652oiAp+TgzIGQQXDlaA+qUoXUjp
+g2KTmoulfQjrgc5CSCk6yA01VxNBqR81JorJx8aD9ApOFVoERlmWhZcR3B/LsVyd
+vYgwFNNkqUh7fyywyy1Z1ijk4SyJVak1VxfdkTTeb1wr5Awjvh82PrdRQfOvctFH
+mVITqdMckdRD3Sx7y8EvypAYpAUiWklNgditetXFjMoV6XyXTPCRkH9zzskrXP6i
+neCyS7xUfEYPYNpabzhpdvkx9Is2PlCJA1fZ1ERZsWcag5vDZa3SHslH5Kh9+ssH
+ps0Ul1j5AgMBAAECggEAHUEzOdBy+oWGwHFhnF4VmT4t91u0npawJYe3EQBckgMF
+FQBtGYYoMHPG2S01KaAc9NnK0AXQCwWEl9Y/kGizhtn3fl67pG9R/mWxw7KGzpMu
+dLAlWhIL7zUCoU8+UhScVpAtZ3OvN6NWDyHPX7hizptmUEIJKM//mx12LeBIvn+P
+8tSiBXxoDGl0JZ+QMzmshOUXLLnxKITgBGL+G9A1qTZHIs6VV7HWH1ptfObulBZf
+yEBK1YBzI6GnBGzLOWnZqGsSbQ717SObQo5rCoRDZB7z4bXNWDEvuH+rqzcs5liu
+af4gmBHNOLGh+Ta5HJ0XeoqU5ANOWlUi95/n2dJufwKBgQDNaMlT1937SHPv/eBq
+Be3MobllTx4vMYh6CtfP8QozTE+sTcmCyvaWVfXwLnQTl//+siefoWsvzW43LaNU
+3A18nCxVFSSbWosBN+0Zo4K9bSpEFGgUrJM5O98zv4+/SzCKFe2562usDzaRiEUW
+iSJkzIUnSlcNc+XCY1rhG9HLXwKBgQDLpS6ATtMDSP9p+XYVMEN2CF8M3xvL roOT
+6wPYfp9fuagMgzNv9GB9SRyM/dM6mN+fkBqLp3EbDZT0UorHsg+YChoyBmctNqpW
+j5/SrVyYe2xoRRgOzUbDstN44/LAhJLQnOXB7S2amo35zZ4FY6sw2w3QfkCildkB
+mY3VhvESpwKBgErLtUPKfxJZN55UG7t/nS++U/wH6z3UE5YdDKizZLt5NinPyWjO
+7yue8Ycb4zifSKA9zx/Zb2Zgr5l4DNmBp4eQdrQklsfbGHLBIp0LZTgE4DcaFyww
+Cwv0OTpmrrlBb9NYWNAyYWqtv3kO3dlu5g8+Sd4cu8YyRZ+a/iSqNKKRAoGBAIPf
+QICYCq8a60Lt5xiLe3QIsbx9EdvQ86Wqz3+3Z28uo3MO1xVNc9pNqO5oRAuzCUSj
+pXz//g9duTKJ7RKp7M0w5Yu1d8TgnGeXdBCScN7RNf9DlvOm3IdH2wdy3TTr5MKw
+h1wQQbLXGM9F5mlpBGeLwqNbznE6hh8yF5XJX30LAoGBAJqaa+yeZskti5ickNTF
+vBBIXyYYBymdxfkf9vDSW1XcZEIVqo3+AGV+qHyTjURaty3QuEhSJEXem/obH5uE
+y37+bnx8Se1IyJ/phYBLwOmtgZoBJALFhvjkFiGTF6naI8E/i4sbi5j/OEyShfWr
+YFZuEKQJhiiMQznfNgthHU6H
+-----END PRIVATE KEY-----
+""", password=None)
+
+ATT_KEY = serialization.load_pem_private_key(b"""
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDT449phHltY2aV
+QIvaT4PUgNS7wDybnnjVO88NGB5PjfUaWY99oDQgOLJlejyVVqRO2wHxLaUMsbuc
+oe0XbgSFJgrnGvG6yPbjSXeIfV5k2dJG60S4Fg2mZ1ieSabuPVKLA03frhbATmIf
+Q+VTMlWLgLVxcT04iqph6VpjehnYke0VPMuN7OM6RsIOEhLcje0bvL4YjTYXH5j4
+mPquc/ZEj/n6WJ6VsS27QygOBbaiGqHs54QnQi4gcgIgUmkR/bl2wL5s+729RBzS
+v1FZfA5gdM9uEG3ogLHOC2uk+1Nuqcdk/tQxd30/2ulXubqDku/nNY2RSJrwD att
+qcyCliANAgMBAAECggEAK3Z7HVbKHZENIsJZrY8v6HAAsv5ssDMicALTpsjytrjU
+tPH4B/nLl2xp03zuXmemTnKIBHOrbl4qsKdaXbr4fGNgSyVwvjKoydhxB3NH4IH5
+qwhpUSVc6Ww7dkR/VFEJ1G/6Ek7AZfPuFqGzsYwalgHxtfJXb3iqGGloXA1Yrd5p
+W2cTEhtSFZP/PQIEK773wYd3aYMw8OCqG2V5bw9N3xwY6KTC0Px8zyBlmAcUBPAj
+QZL/DTGlMdD9+PJ3Ft3Zl2uS7ORn7xfXftvxv5IQdD+JBxV5zUIympKK/7KIVUfH
+dfi93R7rqjL9EOP6bVQkg/WzYRLeVf/8km8HRGqtIQKBgQDxFxQ77t4EQqLPFofN
+oRV7P3lvFqlJDTzAGBnjIT/ujT3SgoFUjRtfG27nWd1lycxv3tE0GTIw0LjJwvmg
+VSFbQbPsmdp+f0jnNIiJayiG591j9Afmw06mnDodaQuSTp7K9idgpnFRGDQzwJHK
+0DwSQzlzEXsPhGnXxpv+2Q+ANQKBgQDg/itVFBw3e5wC8boffi1AgnM97Qa/Y+5B
+I2J9+cZD9iBkvE7kTwVUOI2Rr+XkQmSf+pT6L0yFXhQjIed004rpKVqzTvGL9VXJ
+nBeADS4bxl1jsfkfvq9e6eNUK8vzyLoYQpS5/LK1oG5MPq3+30yzGIHM8JxxaOQ9
+VdKQrUdLeQKBgAh35RAN3eKMbKeVhQOmCtkfa6aJRzz3qBCfSBmAS3yXnXpNdzl/
+E10N26FouKwgoHu1eee4ktjAHB2KKbaGBvvrnORMqy4STn9AiyM4jl3euxoNslFa
+vuJ/TlNGI0/qTw2WA+ATOJu+m+bNdtGG6vVBQz1VedsbrZQUt9oFydOZAoGAMlCk
+4CHfLYk3GnF0bhaJiCOkIfUfzS1L2sVPAV0aOZiRJfX2rpf9WRhMkIgFoUY3uo8P
+QePR+QFQ/4pVeIrWRc45ul+tJN94j92YY8qOxSdXOzRRwgeisFcdv3UL5zi8ZTB+
+khkw3e1CvUpHHvhQ7rxMSsiEM9iBMjY/IJuflgECgYEAqiN3eg8cZjVrYEMcPLGx
+wXknCG0KPc8EpDi1moNwS3z/TcUbfP8vnmT2lFHTAbvVBn+4fcLffkQBoGG3AaSH
+3kc0HXLdy+rFcsXpX7hk9BM/Uey9dqBOAusLS6XxYhcAJ1xOI0kYWoeOhO8fcjNa
+tf26cJGzfbbwf8kfisbv4Uk=
+-----END PRIVATE KEY-----
+""", password=None)
+
+
+def _begin_cert() -> x509.CertificateBuilder:
+ return x509.CertificateBuilder() \
+ .serial_number(1) \
+ .not_valid_before(datetime(2023, 1, 1)) \
+ .not_valid_after(datetime(9999, 12, 31, 23, 59, 59)) # no well-defined expiration date, see RFC5280 4.1.2.5.
+
+
+def generate_chain(ou_fields: List[str]) -> bytes:
+ # First, create the root CA
+ root_name = x509.Name([
+ x509.NameAttribute(NameOID.COMMON_NAME, "qtestsign Root CA - NOT SECURE"),
+ ])
+ # only key_cert_sign=True
+ root_usage = x509.KeyUsage(False, False, False, False, False, True, False, False, False)
+ root_ski = x509.SubjectKeyIdentifier.from_public_key(ROOT_KEY.public_key())
+ root_cert_der = _begin_cert() \
+ .subject_name(root_name) \
+ .issuer_name(root_name) \
+ .public_key(ROOT_KEY.public_key()) \
+ .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True) \
+ .add_extension(root_usage, critical=True) \
+ .add_extension(root_ski, critical=False) \
+ .sign(ROOT_KEY, hashes.SHA256()) \
+ .public_bytes(serialization.Encoding.DER)
+
+ # Now, create the attestation certificate
+ att_name = x509.Name([
+ x509.NameAttribute(NameOID.COMMON_NAME, "qtestsign Attestation CA - NOT SECURE"),
+ *[x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, ou) for ou in ou_fields],
+ ])
+ # only digital_signature=True
+ att_usage = x509.KeyUsage(True, False, False, False, False, False, False, False, False)
+ att_cert_der = _begin_cert() \
+ .subject_name(att_name) \
+ .issuer_name(root_name) \
+ .public_key(ATT_KEY.public_key()) \
+ .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) \
+ .add_extension(att_usage, critical=True) \
+ .add_extension(x509.SubjectKeyIdentifier.from_public_key(ATT_KEY.public_key()), critical=False) \
+ .add_extension(x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(root_ski), critical=False) \
+ .sign(ROOT_KEY, hashes.SHA256()) \
+ .public_bytes(serialization.Encoding.DER)
+
+ # The certificate chain is the attestation and root certificate concatenated
+ # in DER format. Note: The order (first attestation, then root) is important!
+ return att_cert_der + root_cert_der
diff --git a/tools/qcom/mkmbn/elf.py b/tools/qcom/mkmbn/elf.py
new file mode 100644
index 000000000000..a5c4dad5ee01
--- /dev/null
+++ b/tools/qcom/mkmbn/elf.py
@@ -0,0 +1,205 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2021 Stephan Gerhold
+# Data classes are based on the header definitions in the ELF(5) man page.
+# Also see: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+from __future__ import annotations
+
+import dataclasses
+from dataclasses import dataclass
+from struct import Struct
+from typing import List, BinaryIO
+
+
+@dataclass
+class Ehdr:
+ ei_magic: bytes
+ ei_class: int
+ ei_data: int
+ ei_version: int
+ ei_os_abi: int
+ ei_abi_version: int
+ e_type: int
+ e_machine: int
+ e_version: int
+ # Address size specific part
+ e_entry: int = 0
+ e_phoff: int = 0
+ e_shoff: int = 0
+ # End part
+ e_flags: int = 0
+ e_ehsize: int = 0
+ e_phentsize: int = 0
+ e_phnum: int = 0
+ e_shentsize: int = 0
+ e_shnum: int = 0
+ e_shstrndx: int = 0
+
+ START_FORMAT = Struct('<4s5B7xHHL')
+ START_COUNT = 9
+ MEM_FORMAT32 = Struct('<LLL')
+ MEM_FORMAT64 = Struct('<QQQ')
+ MEM_COUNT = 3
+ END_FORMAT = Struct('<L6H')
+ END_COUNT = 7
+
+ CLASS32 = 1
+ CLASS64 = 2
+
+ @staticmethod
+ def parse(b: bytes) -> Ehdr:
+ hdr_unpack = Ehdr.START_FORMAT.unpack_from(b)
+ hdr = Ehdr(*hdr_unpack)
+ assert hdr.ei_magic == b'\x7fELF', f"Invalid ELF header magic: {hdr.ei_magic}"
+ assert hdr.ei_data == 1, "Only little endian supported at the moment"
+ assert hdr.ei_version == 1, f"Unexpected ei_version: {hdr.ei_version}"
+ assert hdr.e_version == 1, f"Unexpected e_version: {hdr.e_version}"
+
+ if hdr.ei_class == Ehdr.CLASS32:
+ mem_format = Ehdr.MEM_FORMAT32
+ else:
+ assert hdr.ei_class == Ehdr.CLASS64, f"Unexpected ei_class: {hdr.ei_class}"
+ mem_format = Ehdr.MEM_FORMAT64
+
+ mem_unpack = mem_format.unpack_from(b, Ehdr.START_FORMAT.size)
+ end_unpack = Ehdr.END_FORMAT.unpack_from(b, Ehdr.START_FORMAT.size + mem_format.size)
+ return Ehdr(*hdr_unpack, *mem_unpack, *end_unpack)
+
+ def save(self, f: BinaryIO) -> int:
+ unpack = dataclasses.astuple(self)
+ written = f.write(Ehdr.START_FORMAT.pack(*unpack[:Ehdr.START_COUNT]))
+
+ if self.ei_class == Ehdr.CLASS32:
+ mem_format = Ehdr.MEM_FORMAT32
+ else:
+ mem_format = Ehdr.MEM_FORMAT64
+ written += f.write(
+ mem_format.pack(*unpack[Ehdr.START_COUNT:Ehdr.START_COUNT + Ehdr.MEM_COUNT]))
+ written += f.write(Ehdr.END_FORMAT.pack(*unpack[-Ehdr.END_COUNT:]))
+ return written
+
+
+@dataclass
+class Phdr:
+ p_type: int
+ p_offset: int
+ p_vaddr: int
+ p_paddr: int
+ p_filesz: int
+ p_memsz: int
+ p_flags: int
+ p_align: int
+
+ data = None
+
+ FORMAT32 = Struct('<8L')
+ FORMAT64 = Struct('<LL6Q')
+
+ PT_NULL = 0
+ PT_LOAD = 1
+
+ @staticmethod
+ def parse(b: bytes, offset: int, ei_class: int) -> Phdr:
+ if ei_class == Ehdr.CLASS32:
+ unpack = list(Phdr.FORMAT32.unpack_from(b, offset))
+ else:
+ unpack = list(Phdr.FORMAT64.unpack_from(b, offset))
+
+ # ELFCLASS64 has flags directly before offset for alignment
+ flags = unpack.pop(1)
+ unpack.insert(-1, flags)
+
+ return Phdr(*unpack)
+
+ def save(self, f: BinaryIO, ei_class: int) -> int:
+ unpack = dataclasses.astuple(self)
+
+ if ei_class == Ehdr.CLASS32:
+ return f.write(Phdr.FORMAT32.pack(*unpack))
+ else:
+ unpack = list(unpack)
+
+ # ELFCLASS64 has flags directly before offset for alignment
+ flags = unpack.pop(-2)
+ unpack.insert(1, flags)
+
+ return f.write(Phdr.FORMAT64.pack(*unpack))
+
+
+def _pad(f: BinaryIO, offset: int, pos: int) -> int:
+ assert offset >= pos, f"{offset} >= {pos}"
+ pad = offset - pos
+ if pad:
+ assert f.write(b'\0' * pad) == pad
+ return offset
+
+
+def align(i: int, alignment: int) -> int:
+ mask = max(alignment - 1, 0)
+ return (i + mask) & ~mask
+
+
+@dataclass
+class Elf:
+ ehdr: Ehdr
+ phdrs: List[Phdr]
+
+ def total_header_size(self):
+ return self.ehdr.e_phoff + len(self.phdrs) * self.ehdr.e_phentsize
+
+ @staticmethod
+ def parse(b: bytes) -> Elf:
+ ehdr = Ehdr.parse(b)
+ view = memoryview(b)
+
+ # Parse program headers
+ phdrs = []
+ offset = ehdr.e_phoff
+ for i in range(ehdr.e_phnum):
+ phdr = Phdr.parse(b, offset, ehdr.ei_class)
+ phdrs.append(phdr)
+
+ # Store data if necessary
+ if phdr.p_filesz and phdr.p_offset:
+ phdr.data = view[phdr.p_offset:phdr.p_offset + phdr.p_filesz]
+
+ offset += ehdr.e_phentsize
+
+ return Elf(ehdr, phdrs)
+
+ def update(self):
+ # Rearrange all segments according to their alignment
+ pos = self.total_header_size()
+ for phdr in sorted(self.phdrs, key=lambda phdr: phdr.p_offset):
+ if phdr.p_offset and phdr.p_filesz:
+ phdr.p_offset = align(pos, phdr.p_align)
+ pos = phdr.p_offset + phdr.p_filesz
+
+ # Ensure program header count is correct
+ self.ehdr.e_phnum = len(self.phdrs)
+
+ # TODO: Clear out sections for now. Those are not read at the moment.
+ # Also, I don't think the Qualcomm firmware loader has any use for these.
+ self.ehdr.e_shoff = 0
+ self.ehdr.e_shnum = 0
+ self.ehdr.e_shstrndx = 0
+
+ def save_header(self, f: BinaryIO) -> int:
+ pos = self.ehdr.save(f)
+ pos = _pad(f, self.ehdr.e_phoff, pos)
+
+ # Write program headers
+ for phdr in self.phdrs:
+ pos += phdr.save(f, self.ehdr.ei_class)
+
+ return pos
+
+ def save(self, f: BinaryIO) -> int:
+ pos = self.save_header(f)
+
+ # Write segment data
+ for phdr in sorted(self.phdrs, key=lambda phdr: phdr.p_offset):
+ if phdr.data:
+ pos = _pad(f, phdr.p_offset, pos)
+ pos += f.write(phdr.data)
+
+ return pos
diff --git a/tools/qcom/mkmbn/hashseg.py b/tools/qcom/mkmbn/hashseg.py
new file mode 100644
index 000000000000..fe74761ae8df
--- /dev/null
+++ b/tools/qcom/mkmbn/hashseg.py
@@ -0,0 +1,356 @@
+# SPDX-License-Identifier: GPL-2.0-only AND BSD-3-Clause
+# Copyright (C) 2021-2023 Stephan Gerhold (GPL-2.0-only)
+# MBN header format adapted from:
+# - signlk: https://git.linaro.org/landing-teams/working/qualcomm/signlk.git
+# - coreboot (util/qualcomm/mbn_tools.py, util/cbfstool/platform_fixups.c)
+# Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. (BSD-3-Clause)
+# See also:
+# - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf
+# - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v2-0.pdf
+from __future__ import annotations
+
+import dataclasses
+import hashlib
+from dataclasses import dataclass
+from io import BytesIO
+from struct import Struct
+
+from . import cert
+from . import elf
+
+# A typical Qualcomm firmware might have the following program headers:
+# LOAD off 0x00000800 vaddr 0x86400000 paddr 0x86400000 align 2**11
+# filesz 0x00001000 memsz 0x00001000 flags rwx
+#
+# The signed version will then look like:
+# NULL off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**0
+# filesz 0x000000e8 memsz 0x00000000 flags --- 7000000
+# NULL off 0x00001000 vaddr 0x86401000 paddr 0x86401000 align 2**12
+# filesz 0x00000988 memsz 0x00001000 flags --- 2200000
+# LOAD off 0x00002000 vaddr 0x86400000 paddr 0x86400000 align 2**11
+# filesz 0x00001000 memsz 0x00001000 flags rwx
+#
+# The second NULL program header with off 0x1000 and filesz 0x988 is the actual
+# "hash table segment" or shortly "hash segment" (see Figure 2 on page 6 in the PDF).
+# It contains the MBN header specified below, then a couple of hashes (e.g. SHA256):
+# 1. Hash of ELF header and program headers
+# 2. Empty hash for hash segment
+# 3. Hashes for data of each memory segment (described by program header)
+# Finally, it contains an RSA signature and the concatenated certificate chain.
+#
+# The first NULL program header is never loaded anywhere, because
+# vaddr = paddr = memsz = 0. However, the "off" and "filesz" cover exactly
+# the ELF header (including all program headers). It is a placeholder so that
+# each hash covers the data of exactly one program header.
+
+# For definitions of the ELF PHDR flags used by Qualcomm, see:
+# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L108-L189
+PHDR_FLAGS_SEGMENT_TYPE_MASK = 0x07000000
+PHDR_FLAGS_SEGMENT_TYPE_SHIFT = 0x18
+PHDR_FLAGS_SEGMENT_TYPE_HASH = (0x2 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT)
+PHDR_FLAGS_SEGMENT_TYPE_HDR = (0x7 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT)
+
+PDHR_FLAGS_ACCESS_TYPE_MASK = 0x00E00000
+PHDR_FLAGS_ACCESS_TYPE_SHIFT = 0x15
+PHDR_FLAGS_ACCESS_TYPE_RO = (0x1 << PHDR_FLAGS_ACCESS_TYPE_SHIFT)
+
+# Flags we use for placeholder for hash over ELF header and hash segment
+PHDR_FLAGS_HDR_PLACEHOLDER = PHDR_FLAGS_SEGMENT_TYPE_HDR
+PHDR_FLAGS_HASH_SEGMENT = (PHDR_FLAGS_SEGMENT_TYPE_HASH | PHDR_FLAGS_ACCESS_TYPE_RO)
+
+EXTRA_PHDRS = 2 # header placeholder + hash segment
+
+# Note: None of the alignments seem to be truly required,
+# this could probably be reduced to get smaller file sizes.
+HASH_SEG_ALIGN = 0x1000
+CERT_CHAIN_ALIGN = 16
+
+# According to the v2.0 PDF the metadata is 128 bytes long, but this does not
+# seem to work. All official firmware seems to use 120 bytes instead.
+MBN_V6_METADATA_SIZE = 120
+
+# See OEM Metadata 2.0 definition in coreboot source code:
+# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691
+MBN_V7_OEM_2_0_METADATA_SIZE = 224
+
+
+@dataclass
+class _HashSegment:
+ image_id: int = 0 # Type of image (unused?)
+ version: int = 0 # Header version number
+
+ hash_size = 0
+ signature_size = 0
+ cert_chain_size = 0
+ total_size = 0
+
+ hashes = []
+ signature = b''
+ cert_chain = b''
+
+ FORMAT = Struct('<10L')
+ Hash = hashlib.sha256
+
+ @property
+ def size_with_header(self):
+ return self.FORMAT.size + self.total_size
+
+ def update(self, dest_addr: int):
+ self.hash_size = len(self.hashes) * self.Hash().digest_size
+ self.signature_size = len(self.signature)
+ self.cert_chain_size = len(self.cert_chain)
+ self.total_size = self.hash_size + self.signature_size + self.cert_chain_size
+
+ def check(self):
+ assert len(self.hashes) * self.Hash().digest_size == self.hash_size
+ assert len(self.signature) == self.signature_size
+ assert len(self.cert_chain) == self.cert_chain_size
+
+ def pack_header(self):
+ self.check()
+ return self.FORMAT.pack(*dataclasses.astuple(self))
+
+ def pack(self):
+ return self.pack_header() \
+ + b''.join(self.hashes) \
+ + self.signature + self.cert_chain
+
+
+@dataclass
+class HashSegmentV3(_HashSegment):
+ version: int = 3 # Header version number
+
+ flash_addr: int = 0 # Location of image in flash (historical)
+ dest_addr: int = 0 # Physical address of loaded hash segment data
+ total_size: int = 0 # = hash_size + signature_size + cert_chain_size
+ hash_size: int = 0 # Size of hashes for all program segments
+ signature_addr: int = 0 # Physical address of loaded attestation signature
+ signature_size: int = 0 # Size of attestation signature
+ cert_chain_addr: int = 0 # Physical address of loaded certificate chain
+ cert_chain_size: int = 0 # Size of certificate chain
+
+ def update(self, dest_addr: int):
+ super().update(dest_addr)
+ self.dest_addr = dest_addr + self.FORMAT.size
+ self.signature_addr = self.dest_addr + self.hash_size
+ self.cert_chain_addr = self.signature_addr + self.signature_size
+
+
+@dataclass
+class HashSegmentV5(_HashSegment):
+ version: int = 5 # Header version number
+
+ signature_size_qcom: int = 0 # Size of signature from Qualcomm
+ cert_chain_size_qcom: int = 0 # Size of certificate chain from Qualcomm
+ total_size: int = 0 # = hash_size + signature_size + cert_chain_size
+ hash_size: int = 0 # Size of hashes for all program segments
+ signature_addr: int = 0xffffffff # unused?
+ signature_size: int = 0 # Size of attestation signature
+ cert_chain_addr: int = 0xffffffff # unused?
+ cert_chain_size: int = 0 # Size of certificate chain
+
+ signature_qcom = b''
+ cert_chain_qcom = b''
+
+ def update(self, dest_addr: int):
+ super().update(dest_addr)
+ self.signature_size_qcom = len(self.signature_qcom)
+ self.cert_chain_size_qcom = len(self.cert_chain_qcom)
+ self.total_size += self.signature_size_qcom + self.cert_chain_size_qcom
+
+ def check(self):
+ super().check()
+ assert len(self.signature_qcom) == self.signature_size_qcom
+ assert len(self.cert_chain_qcom) == self.cert_chain_size_qcom
+
+ def pack(self):
+ return self.pack_header() \
+ + b''.join(self.hashes) \
+ + self.signature_qcom + self.cert_chain_qcom \
+ + self.signature + self.cert_chain
+
+
+@dataclass
+class HashSegmentV6(HashSegmentV5):
+ version: int = 6 # Header version number
+
+ metadata_size_qcom: int = 0 # Size of metadata from Qualcomm
+ metadata_size: int = 0 # Size of metadata
+
+ metadata_qcom = b''
+ metadata = b''
+
+ FORMAT = Struct('<12L')
+ Hash = hashlib.sha384
+
+ def update(self, dest_addr: int):
+ super().update(dest_addr)
+ self.metadata_size_qcom = len(self.metadata_qcom)
+ self.metadata_size = len(self.metadata)
+ self.total_size += self.metadata_size_qcom + self.metadata_size
+
+ def check(self):
+ super().check()
+ assert len(self.metadata_qcom) == self.metadata_size_qcom
+ assert len(self.metadata) == self.metadata_size
+
+ def pack(self):
+ return self.pack_header() \
+ + self.metadata_qcom + self.metadata \
+ + b''.join(self.hashes) \
+ + self.signature_qcom + self.cert_chain_qcom \
+ + self.signature + self.cert_chain
+
+
+@dataclass
+# Information from MBNv7 definition in Coreboot source code:
+# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691
+class HashSegmentV7(_HashSegment):
+ version: int = 7 # Header version number
+
+ common_metadata_size: int = 24 # Size of "common metadata" below
+ metadata_size_qcom: int = 0 # Size of metadata from Qualcomm
+ metadata_size: int = 0 # Size of metadata from OEM
+ hash_size: int = 0 # Size of hashes for all program segments
+ signature_size_qcom: int = 0 # Size of signature from Qualcomm
+ cert_chain_size_qcom: int = 0 # Size of certificate chain from Qualcomm
+ signature_size: int = 0 # Size of attestation signature
+ cert_chain_size: int = 0 # Size of certificate chain
+
+ # Common metadata, placed directly after MBNv7 header
+ common_metadata_major_version: int = 0
+ common_metadata_minor_version: int = 0
+ software_id: int = 0 # Type of software image, mandatory
+ secondary_software_id: int = 0
+ hash_table_algorithm: int = 3 # SHA384
+ measurement_register_target: int = 0
+
+ metadata_qcom = b''
+ metadata = b''
+ signature_qcom = b''
+ cert_chain_qcom = b''
+
+ FORMAT = Struct('<16L')
+ Hash = hashlib.sha384
+
+ def update(self, dest_addr: int):
+ super().update(dest_addr)
+ self.metadata_size_qcom = len(self.metadata_qcom)
+ self.metadata_size = len(self.metadata)
+ self.signature_size_qcom = len(self.signature_qcom)
+ self.cert_chain_size_qcom = len(self.cert_chain_qcom)
+ # self.common_metadata_size is already included as part of the header
+ self.total_size += self.metadata_size_qcom + self.metadata_size
+ self.total_size += self.signature_size_qcom + self.cert_chain_size_qcom
+
+ def check(self):
+ super().check()
+ assert len(self.metadata_qcom) == self.metadata_size_qcom
+ assert len(self.metadata) == self.metadata_size
+ assert len(self.signature_qcom) == self.signature_size_qcom
+ assert len(self.cert_chain_qcom) == self.cert_chain_size_qcom
+
+ def pack(self):
+ return self.pack_header() \
+ + self.metadata_qcom + self.metadata \
+ + b''.join(self.hashes) \
+ + self.signature_qcom + self.cert_chain_qcom \
+ + self.signature + self.cert_chain
+
+HashSegment = {
+ 3: HashSegmentV3,
+ 5: HashSegmentV5,
+ 6: HashSegmentV6,
+ 7: HashSegmentV7,
+}
+
+
+def drop(elff: elf.Elf):
+ # Drop existing hash segments
+ elff.phdrs = [phdr for phdr in elff.phdrs if phdr.p_type != elf.Phdr.PT_NULL
+ or (phdr.p_flags & PHDR_FLAGS_SEGMENT_TYPE_MASK) not in
+ [PHDR_FLAGS_SEGMENT_TYPE_HASH, PHDR_FLAGS_SEGMENT_TYPE_HDR]]
+
+
+def generate(elff: elf.Elf, version: int, sw_id: int):
+ drop(elff)
+ assert elff.phdrs, "Need at least one program header"
+
+ hash_seg = HashSegment[version]()
+
+ if version == 6:
+ # TODO: Figure out metadata format and fill this with useful data
+ hash_seg.metadata = b'\0' * MBN_V6_METADATA_SIZE
+
+ # Software ID is mandatory for MBN v7
+ if version == 7:
+ hash_seg.software_id = sw_id
+ # The format is documented in Coreboot util/qualcomm/mbn_tools.py
+ # (see class Boot_Hdr), but for simplicity we just keep this empty.
+ hash_seg.metadata = b'\0' * MBN_V7_OEM_2_0_METADATA_SIZE
+
+ # Generate hash for all existing segments with data
+ digest_size = hash_seg.Hash().digest_size
+ hash_seg.hashes = [b'\0' * digest_size] * (len(elff.phdrs) + EXTRA_PHDRS)
+ for i, phdr in enumerate(elff.phdrs, start=EXTRA_PHDRS):
+ if phdr.data:
+ hash_seg.hashes[i] = hash_seg.Hash(phdr.data).digest()
+ total_hashes_size = len(hash_seg.hashes) * digest_size
+
+ # Generate certificate chain with specified OU fields (for < v6)
+ # on >= v6 this is part of the metadata instead
+ ou_fields = []
+ if version < 6:
+ ou_fields = [
+ # Note: The SW_ID is checked by the firmware on some platforms (even if secure boot
+ # is disabled), so it must match the firmware type being signed. Everything else seems
+ # to be mostly ignored when secure boot is off and is just added here to match the
+ # documentation and better mimic the official firmware.
+ "01 %016X SW_ID" % sw_id,
+ "02 %016X HW_ID" % 0,
+ "03 %016X DEBUG" % 2, # DISABLED
+ "04 %04X OEM_ID" % 0,
+ "05 %08X SW_SIZE" % (hash_seg.FORMAT.size + total_hashes_size),
+ "06 %04X MODEL_ID" % 0,
+ "07 %04X SHA256" % 1,
+ ]
+ hash_seg.cert_chain = cert.generate_chain(ou_fields)
+ hash_seg.cert_chain = hash_seg.cert_chain.ljust(elf.align(len(hash_seg.cert_chain), CERT_CHAIN_ALIGN), b'\xff')
+ # hash_seg.cert_chain = b'' # uncomment this to omit the certificate chain in the signed image
+
+ # TODO: Generate actual signature with our generated attestation certificate!
+ # There are different signature schemes that could be implemented (RSASSA-PKCS#1 v1.5
+ # RSASSA-PSS, ECDSA over P-384) but it's not entirely clear yet which chipsets supports/
+ # uses which. The signature does not seem to be checked on devices without secure boot,
+ # so just use a dummy value for now.
+ hash_seg.signature = b'\xff' * (cert.ATT_KEY.key_size // 8)
+ # hash_seg.signature = b'' # uncomment this to omit the signature in the signed image
+
+ # Align maximum end address to get address for hash table header, then update header
+ hash_addr = elf.align(max(phdr.p_paddr + phdr.p_memsz for phdr in elff.phdrs), HASH_SEG_ALIGN)
+ hash_seg.update(hash_addr)
+
+ # Insert new hash NULL segment
+ hash_phdr = elf.Phdr(elf.Phdr.PT_NULL, HASH_SEG_ALIGN, hash_addr, hash_addr, hash_seg.size_with_header,
+ elf.align(hash_seg.size_with_header, HASH_SEG_ALIGN),
+ PHDR_FLAGS_HASH_SEGMENT, HASH_SEG_ALIGN)
+ elff.phdrs.insert(0, hash_phdr)
+
+ # Insert new ELF header placeholder program header
+ hdr_hash_phdr = elf.Phdr(elf.Phdr.PT_NULL, 0, 0, 0, 0, 0, PHDR_FLAGS_HDR_PLACEHOLDER, 0)
+ elff.phdrs.insert(0, hdr_hash_phdr)
+
+ # Now determine size of ELF header (including program headers)
+ hdr_hash_phdr.p_filesz = elff.total_header_size()
+
+ # Recompute attributes to match final output (e.g. adjust e_phnum)
+ elff.update()
+
+ # Compute the hash for the ELF header
+ with BytesIO() as hdr_io:
+ elff.save_header(hdr_io)
+ hash_seg.hashes[0] = hash_seg.Hash(hdr_io.getbuffer()).digest()
+
+ # And finally, assemble the hash segment
+ hash_phdr.data = hash_seg.pack()
+ assert len(hash_phdr.data) == hash_phdr.p_filesz
\ No newline at end of file
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 3/6] tools: qcom: add mkmbn.py
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
2026-05-11 13:57 ` [PATCH v7 1/6] config.mk: support vendor generic includes Casey Connolly
2026-05-11 13:57 ` [PATCH v7 2/6] tools: qcom: introduce mkmbn library Casey Connolly
@ 2026-05-11 13:57 ` Casey Connolly
2026-05-11 13:57 ` [PATCH v7 4/6] doc: board/qualcomm: update docs for new u-boot.mbn target Casey Connolly
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
Adjust the elf class to support creating ELF files from scratch so that
mkmbn can build an MBN file from the U-Boot binary image and fix some
imports to work correctly in the U-Boot build system.
The new tool inspects the DTB embedded in u-boot.bin and uses a lookup
table to determine the appropriate configuration based on the root
compatible property, effectively encoding the info that was previously
kept in documentation.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
tools/mkmbn | 1 +
tools/qcom/mkmbn/elf.py | 36 ++++++++++
tools/qcom/mkmbn/hashseg.py | 4 +-
tools/qcom/mkmbn/mkmbn.py | 165 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 204 insertions(+), 2 deletions(-)
diff --git a/tools/mkmbn b/tools/mkmbn
new file mode 120000
index 000000000000..a7b2096756f7
--- /dev/null
+++ b/tools/mkmbn
@@ -0,0 +1 @@
+qcom/mkmbn/mkmbn.py
\ No newline at end of file
diff --git a/tools/qcom/mkmbn/elf.py b/tools/qcom/mkmbn/elf.py
index a5c4dad5ee01..4bd54239fbbe 100644
--- a/tools/qcom/mkmbn/elf.py
+++ b/tools/qcom/mkmbn/elf.py
@@ -44,8 +44,24 @@ class Ehdr:
CLASS32 = 1
CLASS64 = 2
+ # Init a qcom XBL style ELF header
+ def __init__(self):
+ self.ei_magic = b"\x7fELF"
+ self.ei_class = 2
+ self.ei_data = 1
+ self.ei_version = 1
+ self.ei_os_abi = 0
+ self.ei_abi_version = 0
+ self.e_type = 2
+ self.e_machine = 183
+ self.e_version = 1
+
+ self.e_ehsize = 64
+ self.e_phoff = 64
+ self.e_phentsize = 56
+
@staticmethod
def parse(b: bytes) -> Ehdr:
hdr_unpack = Ehdr.START_FORMAT.unpack_from(b)
hdr = Ehdr(*hdr_unpack)
@@ -109,8 +125,24 @@ class Phdr:
unpack.insert(-1, flags)
return Phdr(*unpack)
+ @staticmethod
+ def from_bin(b: bytes, loadaddr: int) -> Phdr:
+ # p_offset is fixed later
+ phdr = Phdr(
+ p_type=1,
+ p_offset=0xFFFFFFFF,
+ p_vaddr=loadaddr,
+ p_paddr=loadaddr,
+ p_filesz=len(b),
+ p_memsz=len(b),
+ p_flags=7,
+ p_align=0x1000,
+ )
+ phdr.data = memoryview(b)
+ return phdr
+
def save(self, f: BinaryIO, ei_class: int) -> int:
unpack = dataclasses.astuple(self)
if ei_class == Ehdr.CLASS32:
@@ -142,8 +174,12 @@ def align(i: int, alignment: int) -> int:
class Elf:
ehdr: Ehdr
phdrs: List[Phdr]
+ def __init__(self):
+ self.ehdr = Ehdr()
+ self.phdrs: List[Phdr] = []
+
def total_header_size(self):
return self.ehdr.e_phoff + len(self.phdrs) * self.ehdr.e_phentsize
@staticmethod
diff --git a/tools/qcom/mkmbn/hashseg.py b/tools/qcom/mkmbn/hashseg.py
index fe74761ae8df..db157a23d186 100644
--- a/tools/qcom/mkmbn/hashseg.py
+++ b/tools/qcom/mkmbn/hashseg.py
@@ -14,10 +14,10 @@ import hashlib
from dataclasses import dataclass
from io import BytesIO
from struct import Struct
-from . import cert
-from . import elf
+import cert
+import elf
# A typical Qualcomm firmware might have the following program headers:
# LOAD off 0x00000800 vaddr 0x86400000 paddr 0x86400000 align 2**11
# filesz 0x00001000 memsz 0x00001000 flags rwx
diff --git a/tools/qcom/mkmbn/mkmbn.py b/tools/qcom/mkmbn/mkmbn.py
new file mode 100755
index 000000000000..e4484b539b02
--- /dev/null
+++ b/tools/qcom/mkmbn/mkmbn.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2024 Stephan Gerhold
+# Copyright (C) 2026 Casey Connolly
+#
+# This is a port of qtestsign designed to integrate with the
+# U-Boot build system. See the qtestsign repo for more information.
+# https://github.com/msm8916-mainline/qtestsign
+#
+from __future__ import annotations
+
+import argparse
+from pathlib import Path
+
+from elf import Elf, Phdr
+import hashseg
+import sys
+from enum import Enum
+import struct
+
+verbose = False
+
+def log(*args, **kwargs):
+ if verbose:
+ print(*args, *kwargs, file=sys.stderr)
+
+def error(*args, **kwargs):
+ print("mkmbn: ", file=sys.stderr, end='')
+ print(*args, *kwargs, file=sys.stderr)
+
+class SwId(Enum):
+ sbl1 = 0x00
+ mba = 0x01
+ modem = 0x02
+ prog = 0x03
+ adsp = 0x04
+ devcfg = 0x05
+ tz = 0x07
+ aboot = 0x09
+ uefi = 0x09
+ rpm = 0x0A
+ tz_app = 0x0C
+ wcnss = 0x0D
+ venus = 0x0E
+ wlanmdsp = 0x12
+ gpu = 0x14
+ hyp = 0x15
+ cdsp = 0x17
+ slpi = 0x18
+ abl = 0x1C
+ cmnlib = 0x1F
+ aop = 0x21
+ qup = 0x24
+ xbl_config = 0x25
+
+class MbnData:
+
+ # sw_id 0x9 is aboot/uefi, the most common
+ def __init__(self, loadaddr: int, version: int, sw_id: SwId = SwId.aboot):
+ self.loadaddr = loadaddr
+ self.version = version
+ self.sw_id = sw_id
+
+
+"""
+This dictionary is used to map a board or platform to the appropriate load address and
+other MBN metadata. When adding support for a new platform to U-Boot, the appropriate
+data should be filled out here. The load address can typically be determined by looking
+at the uefi.elf or xbl.elf for the platform. For the uefi.elf it is the load address, and
+for xbl.elf it is typically the RWX section in the middle, just BEFORE the section loaded
+at 0x1495xxxx or similar. Looking at similar platforms in the table below may help.
+"""
+boards: dict[bytes, MbnData] = {
+ # Exact matches for boards, these are preferred
+ b"qcom,qcs6490-rb3gen2\0": MbnData(0x9FC00000, 6, SwId.uefi),
+ b"qcom,qcs9100-ride-r3\0": MbnData(0xAF000000, 6, SwId.uefi), # Dragonwing IQ9
+ b"qcom,qcs8300-ride\0": MbnData(0xAF000000, 6, SwId.uefi), # Dragonwing IQ8
+ b"qcom,qcs615-ride\0": MbnData(0x9FC00000, 6, SwId.uefi), # Dragonwing IQ6
+ # Fallback/generic matches since most boards for a platform will
+ # use the same load address
+ b"qcom,qcm6490\0": MbnData(0x9FC00000, 6, SwId.uefi), # rb3gen2, rubikpi3
+ b"qcom,qcs9100\0": MbnData(0xAF000000, 6, SwId.uefi), # Dragonwing IQ9
+ b"qcom,qcs8300\0": MbnData(0xAF000000, 6, SwId.uefi), # Dragonwing IQ8
+ b"qcom,qcs8550\0": MbnData(0xA7000000, 7, SwId.uefi), # C8550
+ b"qcom,sm8550\0": MbnData(0xA7000000, 7, SwId.uefi), # C8550
+ b"qcom,sm8650\0": MbnData(0xA7000000, 7, SwId.uefi), # SM8650
+ b"qcom,qcs615\0": MbnData(0x9FC00000, 6, SwId.uefi), # Dragonwing IQ6
+ b"qcom,ipq5424\0": MbnData(0x8a380000, 6, SwId.aboot),
+ b"qcom,ipq9574\0": MbnData(0x4A240000, 6, SwId.aboot),
+
+ # msm8916/apq8016 has an "aboot" partition but the process is the same
+ # They use header version 3.
+ b"qcom,apq8016\0": MbnData(0x8f600000, 3, SwId.aboot),
+ b"qcom,msm8916\0": MbnData(0x8f600000, 3, SwId.aboot),
+}
+
+parser = argparse.ArgumentParser(
+ description="""
+ Create a signed Qualcomm "uefi" ELF image
+"""
+)
+parser.register("type", "hex", lambda s: int(s, 16))
+parser.add_argument(
+ "-o", "--output", type=Path, default="u-boot.mbn", help="Output file"
+)
+parser.add_argument(
+ "-v", dest="verbose", action="store_true", default=False, help="Verbose"
+)
+parser.add_argument(
+ "bin", type=argparse.FileType("rb"), help="Binary to embed (e.g. u-boot.bin)"
+)
+args = parser.parse_args()
+verbose = args.verbose
+
+elf = Elf()
+
+data: bytes = args.bin.read()
+
+# dtb is at the end, so find the last match
+dtb_off = 0
+off = 0
+dtb_size = 0
+while True:
+ off = data.find(b"\xd0\x0d\xfe\xed", dtb_off + dtb_size)
+ if off == -1:
+ break
+ (dtb_size,) = struct.unpack_from("I", data, offset=off)
+ dtb_off = off
+
+if not dtb_off:
+ print("Couldn't find DTB in provided binary!")
+ exit(1)
+
+log(f"Found FDT at {dtb_off:#x} size {dtb_size:#x}")
+
+mbn: MbnData|None = None
+
+for match, mbndata in boards.items():
+ if data.find(match, dtb_off) != -1:
+ mbn = mbndata
+ break
+
+if not mbn:
+ error(
+ "CONFIG_QCOM_GENERATE_MBN is enabled but this platform doesn't appear to be supported\n"
+ "Please see tools/qcom/mkmbn/mkmbn.py for details. If you intend to chainload U-Boot\n"
+ "then disregard this message and disable CONFIG_QCOM_GENERATE_MBN in your defconfig."
+ )
+ args.output.unlink(missing_ok=True)
+ exit(1)
+
+log(f"Detected board {match.decode('UTF-8')} with load address {mbn.loadaddr:#x}")
+
+elf.phdrs.append(Phdr.from_bin(data, mbn.loadaddr))
+elf.ehdr.e_entry = mbn.loadaddr
+elf.update()
+
+# QLI boards use v6 sw_id is "aboot"
+hashseg.generate(elf, mbn.version, mbn.sw_id.value)
+# print(f"after: {elf}")
+
+with open(args.output, "wb") as f:
+ elf.save(f)
+
+log(f"Built signed MBN: {args.output.resolve()}")
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 4/6] doc: board/qualcomm: update docs for new u-boot.mbn target
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
` (2 preceding siblings ...)
2026-05-11 13:57 ` [PATCH v7 3/6] tools: qcom: add mkmbn.py Casey Connolly
@ 2026-05-11 13:57 ` Casey Connolly
2026-05-11 13:57 ` [PATCH v7 5/6] mach-snapdragon: add kconfig infra for building MBN files Casey Connolly
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
Update the build docs to describe building the u-boot.mbn target
explicitly for some boards. Additionally add a new "signing" page to
describe the purpose of mkmbn and the MBN format.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
doc/board/qualcomm/board.rst | 2 +-
doc/board/qualcomm/dragonboard410c.rst | 19 ++++++-------------
doc/board/qualcomm/dragonwing.rst | 14 ++++----------
doc/board/qualcomm/index.rst | 1 +
doc/board/qualcomm/rb3gen2.rst | 30 ++++++++++++++----------------
doc/board/qualcomm/rdp.rst | 5 +++--
doc/board/qualcomm/signing.rst | 29 +++++++++++++++++++++++++++++
7 files changed, 58 insertions(+), 42 deletions(-)
diff --git a/doc/board/qualcomm/board.rst b/doc/board/qualcomm/board.rst
index eb800f8c535a..18119cec5b13 100644
--- a/doc/board/qualcomm/board.rst
+++ b/doc/board/qualcomm/board.rst
@@ -103,9 +103,9 @@ Use the following commands::
make CROSS_COMPILE=aarch64-linux-gnu- O=.output -j$(nproc)
Or for db410c (and other boards not supported by the generic target)::
- make CROSS_COMPILE=aarch64-linux-gnu- O=.output dragonboard410c_defconfig
+ make CROSS_COMPILE=aarch64-linux-gnu- O=.output qcom_dragonboard410c_defconfig
make CROSS_COMPILE=aarch64-linux-gnu- O=.output -j$(nproc)
Or for smartphones::
diff --git a/doc/board/qualcomm/dragonboard410c.rst b/doc/board/qualcomm/dragonboard410c.rst
index 34629241110c..cc2d28e9dfda 100644
--- a/doc/board/qualcomm/dragonboard410c.rst
+++ b/doc/board/qualcomm/dragonboard410c.rst
@@ -20,28 +20,21 @@ Installation
------------
First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``dragonboard410c``::
$ export CROSS_COMPILE=<aarch64 toolchain prefix>
- $ make dragonboard410c_defconfig
+ $ make qcom_dragonboard410c_defconfig
$ make
-This will build ``u-boot.elf`` in the configured output directory.
+This will build ``u-boot.mbn`` in the configured output directory.
Although the DragonBoard 410c does not have secure boot set up by default,
-the firmware still expects firmware ELF images to be "signed". The signature
-does not provide any security in this case, but it provides the firmware with
-some required metadata.
+the firmware still expects firmware ELF images to be "signed". This is
+handled automatically with mkmbn (see :doc:`signing` for more details).
-To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_::
-
- $ ./qtestsign.py aboot u-boot.elf
-
-Then install the resulting ``u-boot-test-signed.mbn`` to the ``aboot`` partition
-on your device, e.g. with ``fastboot flash aboot u-boot-test-signed.mbn``.
+Then install the resulting ``u-boot.mbn`` to the ``aboot`` partition
+on your device, e.g. with ``fastboot flash aboot u-boot.mbn``.
U-Boot should be running after a reboot (``fastboot reboot``).
-.. _qtestsign: https://github.com/msm8916-mainline/qtestsign
-
Usage
-----
Press Volume Down during boot to enter Fastboot mode.
diff --git a/doc/board/qualcomm/dragonwing.rst b/doc/board/qualcomm/dragonwing.rst
index d48994153095..028ec340d155 100644
--- a/doc/board/qualcomm/dragonwing.rst
+++ b/doc/board/qualcomm/dragonwing.rst
@@ -19,20 +19,15 @@ Installation
First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``QCS615``, ``QCS8300`` or ``QCS9100``::
$ export CROSS_COMPILE=<aarch64 toolchain prefix>
$ make qcom_qcs8300_defconfig
- $ make -j8 u-boot.mbn
+ $ make -j8
Although the board does not have secure boot set up by default,
-the firmware still expects firmware ELF images to be "signed". The signature
-does not provide any security in this case, but it provides the firmware with
-some required metadata.
+the firmware still expects firmware ELF images to be "signed" in the MBN format.
+This is handled automatically with mkmbn (see :doc:`signing` for more details).
-To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_::
-
- $ qtestsign -v6 aboot -o u-boot.mbn u-boot.elf
-
-Then flash the resulting ``u-boot.mbn`` to the ``uefi_a`` partition
+Just flash the resulting ``u-boot.mbn`` to the ``uefi_a`` partition
on your device with ``fastboot flash uefi_a u-boot.mbn``.
U-Boot should be running after a reboot (``fastboot reboot``).
@@ -43,7 +38,6 @@ A tool like bkerler's `edl`_ can be used for flashing with the firehose loader (
the firehose loader can be obtained from `dragonwing IQ9 bootbinaries`.) ::
$ edl.py --loader /path/to/prog_firehose_ddr.elf w uefi_a u-boot.mbn
-.. _qtestsign: https://github.com/msm8916-mainline/qtestsign
.. _edl: https://github.com/bkerler/edl
.. _dragonwing IQ9 bootbinaries: https://artifacts.codelinaro.org/ui/native/qli-ci/flashable-binaries/qimpsdk/qcs9075-rb8-core-kit
diff --git a/doc/board/qualcomm/index.rst b/doc/board/qualcomm/index.rst
index 3238a68e859b..b7637b3c0439 100644
--- a/doc/board/qualcomm/index.rst
+++ b/doc/board/qualcomm/index.rst
@@ -13,4 +13,5 @@ Qualcomm
rb3gen2
iq8
phones
rdp
+ signing
diff --git a/doc/board/qualcomm/rb3gen2.rst b/doc/board/qualcomm/rb3gen2.rst
index 518d01c4c3a3..329baa8d1f3a 100644
--- a/doc/board/qualcomm/rb3gen2.rst
+++ b/doc/board/qualcomm/rb3gen2.rst
@@ -17,34 +17,32 @@ Installation
First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``qcm6490``::
$ export CROSS_COMPILE=<aarch64 toolchain prefix>
$ make qcm6490_defconfig
- $ make -j8
+ $ make -j8 DEVICE_TREE=qcom/qcs6490-rb3gen2
-This will build ``u-boot.elf`` in the configured output directory.
+This will build ``u-boot.mbn`` in the configured output directory.
-Although the RB3 Gen 2 does not have secure boot set up by default,
-the firmware still expects firmware ELF images to be "signed". The signature
-does not provide any security in this case, but it provides the firmware with
-some required metadata.
-
-To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_::
-
- $ qtestsign -v6 aboot -o u-boot.mbn u-boot.elf
+Although the board does not have secure boot set up by default,
+the firmware still expects firmware ELF images to be "signed" in the MBN format.
+This is handled automatically with mkmbn (see :doc:`signing` for more details).
Then install the resulting ``u-boot.mbn`` to the ``uefi_a`` partition
on your device with ``fastboot flash uefi_a u-boot.mbn``.
U-Boot should be running after a reboot (``fastboot reboot``).
-Note that fastboot is not yet supported in U-Boot on this board, as a result,
-to flash back the original firmware, or new versoins of the U-Boot, EDL mode
-must be used. This can be accessed by pressing the EDL mode button as described
-in the Qualcomm Linux documentation. A tool like bkerler's `edl`_ can be used
-for flashing with the firehose loader binary appropriate for the board.
+Note that fastboot is not yet supported in U-Boot on this board, as a result, to flash
+back the original firmware, or new versoins of the U-Boot, EDL mode must be used. This
+can be accessed by holding the EDL button while powering on as described in the
+Qualcomm Linux documentation.
+
+A tool like bkerler's `edl`_ can be used for flashing with the firehose loader from the `RB3 Gen 2 bootbinaries`. ::
+
+ $ edl.py --loader /path/to/prog_firehose_ddr.elf w uefi_a u-boot.mbn
-.. _qtestsign: https://github.com/msm8916-mainline/qtestsign
.. _edl: https://github.com/bkerler/edl
+.. _RB3 Gen 2 bootbinaries: https://artifacts.codelinaro.org/artifactory/qli-ci/software/chip/qualcomm_linux-spf-1-0/qualcomm-linux-spf-1-0_test_device_public/r1.0_00039.2/QCM6490.LE.1.0/common/build/ufs/bin/QCM6490_bootbinaries.zip
Usage
-----
diff --git a/doc/board/qualcomm/rdp.rst b/doc/board/qualcomm/rdp.rst
index 99cf8eba57ce..4e63fe624b8a 100644
--- a/doc/board/qualcomm/rdp.rst
+++ b/doc/board/qualcomm/rdp.rst
@@ -16,11 +16,12 @@ Installation
First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``IPQ9574``::
$ export CROSS_COMPILE=<aarch64 toolchain prefix>
$ make qcom_ipq9574_mmc_defconfig
- $ make -j8
+ $ make -j8 u-boot.mbn
-This will build ``u-boot.elf`` in the configured output directory.
+This will build the signed ``u-boot.mbn`` in the configured output directory. More information
+about image signing can be found in :doc:`signing`.
The firmware expects the ELF images to be in MBN format. The `elftombn.py` tool
can be used to convert the ELF images to MBN format.
diff --git a/doc/board/qualcomm/signing.rst b/doc/board/qualcomm/signing.rst
new file mode 100644
index 000000000000..317cd57cefee
--- /dev/null
+++ b/doc/board/qualcomm/signing.rst
@@ -0,0 +1,29 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. sectionauthor:: Casey Connolly <casey.connolly@linaro.org>
+
+Qualcomm Image Signing
+======================
+
+On some boards like the RB3 Gen 2 where U-Boot runs as the first stage bootloader,
+it must be in a Qualcomm specific signed ELF format called ``mbn``.
+
+For most boards this is handled automatically with the ``mkmbn`` tool in the U-Boot
+build system. If you're bringing up a new platform which will run U-Boot as the first
+stage bootloader, you may need to add your board and platform compatible string and
+the load address used by your board to the ``boards`` table in ``tools/qcom/mkmbn/mkmbn.py``.
+
+For example:
+
+.. code-block:: python
+
+ boards: dict[bytes, int] = {
+ # Exact matches for boards, these are preferred
+ # Don't forget the null terminator!
+ b"qcom,qcs6490-rb3gen2\0": MbnData(0x9FC00000, 6, SwId.aboot),
+ ...
+ }
+
+
+When you run make to build the ``u-boot.mbn`` target, ``mkmbn`` will inspect the DTB in your
+U-Boot image and try to match the compatible to the table, then it will build an ELF image and
+hash/sign it per the MBN spec.
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 5/6] mach-snapdragon: add kconfig infra for building MBN files
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
` (3 preceding siblings ...)
2026-05-11 13:57 ` [PATCH v7 4/6] doc: board/qualcomm: update docs for new u-boot.mbn target Casey Connolly
@ 2026-05-11 13:57 ` Casey Connolly
2026-06-08 14:28 ` Tom Rini
2026-05-11 13:57 ` [PATCH v7 6/6] configs: qcom: use mkmbn and stop building ELF files Casey Connolly
2026-07-20 13:10 ` [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
6 siblings, 1 reply; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
Add a qualcomm specific Makefile fragment to make u-boot.mbn a build
target and introduce a kconfig option to build it by default on relevant
platforms via CONFIG_BUILD_TARGET.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
Kconfig | 1 +
arch/arm/mach-snapdragon/Kconfig | 11 +++++++++++
board/qualcomm/config.mk | 14 ++++++++++++++
3 files changed, 26 insertions(+)
diff --git a/Kconfig b/Kconfig
index 8428d039a1c0..183c8096030d 100644
--- a/Kconfig
+++ b/Kconfig
@@ -557,8 +557,9 @@ config BUILD_TARGET
default "u-boot-with-spl.sfp" if ARCH_SOCFPGA_GEN5
default "u-boot.itb" if !BINMAN && SPL_LOAD_FIT && (ARCH_ROCKCHIP || \
RISCV || ARCH_ZYNQMP)
default "u-boot.kwb" if (ARCH_KIRKWOOD || ARMADA_32BIT) && !SPL
+ default "u-boot.mbn" if ARCH_SNAPDRAGON && QCOM_GENERATE_MBN
help
Some SoCs need special image types (e.g. U-Boot binary
with a special header) as build targets. By defining
CONFIG_BUILD_TARGET in the SoC / board header, this
diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig
index d3de8693b5a8..6329cbb9215e 100644
--- a/arch/arm/mach-snapdragon/Kconfig
+++ b/arch/arm/mach-snapdragon/Kconfig
@@ -41,5 +41,16 @@ config SYS_CONFIG_NAME
This option contains information about board configuration name.
Based on this option include/configs/<CONFIG_SYS_CONFIG_NAME>.h header
will be used for board configuration.
+config QCOM_GENERATE_MBN
+ bool "Generate an MBN-compatible ELF binary"
+ default n
+ help
+ Enable this if you intend to flash U-Boot as a first-stage bootloader.
+ The build system will generate a board-specific ELF file with the appropriate
+ MBN hash segments and test keys.
+
+ New platforms can be added to tools/qcom/mkmbn/mkmbn.py if they aren't already
+ supported.
+
endif
diff --git a/board/qualcomm/config.mk b/board/qualcomm/config.mk
new file mode 100644
index 000000000000..769e4a51ca01
--- /dev/null
+++ b/board/qualcomm/config.mk
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright Linaro Ltd.
+#
+# Qualcomm specific make target for MBN signed ELF files.
+#
+
+# Create Qualcomm signed elf images
+CMD_MKMBN = $(srctree)/tools/qcom/mkmbn/mkmbn.py
+quiet_cmd_mkmbn = MBN $@
+ cmd_mkmbn = $(CMD_MKMBN) $<
+
+u-boot.mbn: u-boot.bin FORCE
+ $(call if_changed,mkmbn)
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 6/6] configs: qcom: use mkmbn and stop building ELF files
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
` (4 preceding siblings ...)
2026-05-11 13:57 ` [PATCH v7 5/6] mach-snapdragon: add kconfig infra for building MBN files Casey Connolly
@ 2026-05-11 13:57 ` Casey Connolly
2026-05-21 12:23 ` Stephan Gerhold
2026-07-20 13:10 ` [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
6 siblings, 1 reply; 11+ messages in thread
From: Casey Connolly @ 2026-05-11 13:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Casey Connolly, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
With mkmbn integrated we now no longer need CONFIG_REMAKE_ELF to then
run qtestsign and can instead just enable QCOM_GENERATE_MBN to have
mkmbn emit an MBN file directly.
While we're here, finally rename the db410c and 820c defconfigs to have
the qcom_ prefix.
Tested on qcs6490 and sm8550.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
configs/qcm6490_defconfig | 2 +-
configs/{dragonboard410c_defconfig => qcom_dragonboard410c_defconfig} | 2 +-
configs/{dragonboard820c_defconfig => qcom_dragonboard820c_defconfig} | 0
configs/qcom_ipq9574_mmc_defconfig | 2 +-
configs/qcom_qcs615_defconfig | 2 +-
configs/qcom_qcs8300_defconfig | 2 +-
configs/qcom_qcs9100_defconfig | 2 +-
7 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig
index b088367f86c0..56ec771ba1e4 100644
--- a/configs/qcm6490_defconfig
+++ b/configs/qcm6490_defconfig
@@ -9,9 +9,9 @@
CONFIG_ARM=y
# Address where U-Boot will be loaded
CONFIG_TEXT_BASE=0x9fc00000
-CONFIG_REMAKE_ELF=y
+CONFIG_QCOM_GENERATE_MBN=y
CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs6490-rb3gen2"
CONFIG_FASTBOOT_BUF_ADDR=0xd8800000
diff --git a/configs/dragonboard410c_defconfig b/configs/qcom_dragonboard410c_defconfig
similarity index 98%
rename from configs/dragonboard410c_defconfig
rename to configs/qcom_dragonboard410c_defconfig
index 51ad265688c4..e93f5f3094f4 100644
--- a/configs/dragonboard410c_defconfig
+++ b/configs/qcom_dragonboard410c_defconfig
@@ -11,9 +11,9 @@ CONFIG_ENV_OFFSET=0x0
CONFIG_DEFAULT_DEVICE_TREE="qcom/apq8016-sbc"
CONFIG_OF_LIBFDT_OVERLAY=y
CONFIG_SYS_LOAD_ADDR=0x80080000
CONFIG_IDENT_STRING="\nQualcomm-DragonBoard 410C"
-CONFIG_REMAKE_ELF=y
+CONFIG_QCOM_GENERATE_MBN=y
CONFIG_BUTTON_CMD=y
CONFIG_FIT=y
CONFIG_BOOTSTD_FULL=y
CONFIG_OF_BOARD_SETUP=y
diff --git a/configs/dragonboard820c_defconfig b/configs/qcom_dragonboard820c_defconfig
similarity index 100%
rename from configs/dragonboard820c_defconfig
rename to configs/qcom_dragonboard820c_defconfig
diff --git a/configs/qcom_ipq9574_mmc_defconfig b/configs/qcom_ipq9574_mmc_defconfig
index 6592d9de39da..98614f6196b2 100644
--- a/configs/qcom_ipq9574_mmc_defconfig
+++ b/configs/qcom_ipq9574_mmc_defconfig
@@ -11,9 +11,9 @@ CONFIG_DEFAULT_DEVICE_TREE="qcom/ipq9574-rdp433"
CONFIG_SYS_LOAD_ADDR=0x50000000
CONFIG_DEBUG_UART_BASE=0x78b1000
CONFIG_DEBUG_UART_CLOCK=1843200
CONFIG_DEBUG_UART=y
-CONFIG_REMAKE_ELF=y
+CONFIG_QCOM_GENERATE_MBN=y
# CONFIG_EFI_LOADER is not set
CONFIG_FIT=y
CONFIG_FIT_VERBOSE=y
# CONFIG_BOOTSTD is not set
diff --git a/configs/qcom_qcs615_defconfig b/configs/qcom_qcs615_defconfig
index 27666a8129d7..f4760703c94a 100644
--- a/configs/qcom_qcs615_defconfig
+++ b/configs/qcom_qcs615_defconfig
@@ -15,9 +15,9 @@ CONFIG_DEBUG_UART_MSM_GENI=y
CONFIG_DEBUG_UART_CLOCK=7372800
CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs615-ride"
-CONFIG_REMAKE_ELF=y
+CONFIG_QCOM_GENERATE_MBN=y
# Address where U-Boot will be loaded
CONFIG_TEXT_BASE=0x9fc00000
diff --git a/configs/qcom_qcs8300_defconfig b/configs/qcom_qcs8300_defconfig
index 5fffcddc16e9..8ff790f2889d 100644
--- a/configs/qcom_qcs8300_defconfig
+++ b/configs/qcom_qcs8300_defconfig
@@ -15,7 +15,7 @@ CONFIG_DEBUG_UART_MSM_GENI=y
CONFIG_DEBUG_UART_CLOCK=14745600
# Address where U-Boot will be loaded
CONFIG_TEXT_BASE=0xaf000000
-CONFIG_REMAKE_ELF=y
+CONFIG_QCOM_GENERATE_MBN=y
CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs8300-ride"
diff --git a/configs/qcom_qcs9100_defconfig b/configs/qcom_qcs9100_defconfig
index 082106157bbc..84b2b9a1bd1b 100644
--- a/configs/qcom_qcs9100_defconfig
+++ b/configs/qcom_qcs9100_defconfig
@@ -6,9 +6,9 @@
#include "qcom_defconfig"
# Address where U-Boot will be loaded
CONFIG_TEXT_BASE=0xaf000000
-CONFIG_REMAKE_ELF=y
+CONFIG_QCOM_GENERATE_MBN=y
CONFIG_FASTBOOT_BUF_ADDR=0xdb300000
CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs9100-ride-r3"
CONFIG_ENV_IS_IN_SCSI=y
CONFIG_ENV_SCSI_PART_UUID="71cb9cd0-acf1-b6cb-ad91-be9572fe11a9"
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v7 6/6] configs: qcom: use mkmbn and stop building ELF files
2026-05-11 13:57 ` [PATCH v7 6/6] configs: qcom: use mkmbn and stop building ELF files Casey Connolly
@ 2026-05-21 12:23 ` Stephan Gerhold
0 siblings, 0 replies; 11+ messages in thread
From: Stephan Gerhold @ 2026-05-21 12:23 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, Sumit Garg, u-boot-qcom, Tom Rini, Neil Armstrong,
Balaji Selvanathan, Varadarajan Narayanan, Quentin Schulz,
Heinrich Schuchardt, Marek Vasut, Peng Fan, Jaehoon Chung,
Aswin Murugan, Ilias Apalodimas, Michal Simek, David Lechner
On Mon, May 11, 2026 at 03:57:48PM +0200, Casey Connolly wrote:
> With mkmbn integrated we now no longer need CONFIG_REMAKE_ELF to then
> run qtestsign and can instead just enable QCOM_GENERATE_MBN to have
> mkmbn emit an MBN file directly.
>
> While we're here, finally rename the db410c and 820c defconfigs to have
> the qcom_ prefix.
>
> Tested on qcs6490 and sm8550.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> [...]
> configs/{dragonboard410c_defconfig => qcom_dragonboard410c_defconfig} | 2 +-
. [...]
> 7 files changed, 6 insertions(+), 6 deletions(-)
>
> [...]
> diff --git a/configs/dragonboard410c_defconfig b/configs/qcom_dragonboard410c_defconfig
> similarity index 98%
> rename from configs/dragonboard410c_defconfig
> rename to configs/qcom_dragonboard410c_defconfig
> index 51ad265688c4..e93f5f3094f4 100644
> --- a/configs/dragonboard410c_defconfig
> +++ b/configs/qcom_dragonboard410c_defconfig
> @@ -11,9 +11,9 @@ CONFIG_ENV_OFFSET=0x0
> CONFIG_DEFAULT_DEVICE_TREE="qcom/apq8016-sbc"
> CONFIG_OF_LIBFDT_OVERLAY=y
> CONFIG_SYS_LOAD_ADDR=0x80080000
> CONFIG_IDENT_STRING="\nQualcomm-DragonBoard 410C"
> -CONFIG_REMAKE_ELF=y
> +CONFIG_QCOM_GENERATE_MBN=y
> CONFIG_BUTTON_CMD=y
> CONFIG_FIT=y
> CONFIG_BOOTSTD_FULL=y
> CONFIG_OF_BOARD_SETUP=y
Thanks, this simplifies testing U-Boot changes a lot!
Tested-by: Stephan Gerhold <stephan.gerhold@linaro.org> # db410c
Thanks,
Stephan
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 1/6] config.mk: support vendor generic includes
2026-05-11 13:57 ` [PATCH v7 1/6] config.mk: support vendor generic includes Casey Connolly
@ 2026-06-08 14:27 ` Tom Rini
0 siblings, 0 replies; 11+ messages in thread
From: Tom Rini @ 2026-06-08 14:27 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, Sumit Garg, u-boot-qcom, Neil Armstrong,
Balaji Selvanathan, Varadarajan Narayanan, Quentin Schulz,
Heinrich Schuchardt, Marek Vasut, Peng Fan, Jaehoon Chung,
Aswin Murugan, Ilias Apalodimas, Michal Simek, David Lechner
[-- Attachment #1: Type: text/plain, Size: 537 bytes --]
On Mon, May 11, 2026 at 03:57:43PM +0200, Casey Connolly wrote:
> Currently only board/vendor/$(BOARD)/config.mk is supported, add the
> additional usecase of having a vendor generic config.mk for adding
> functionality like platform-specific build targets.
>
> Additionally, fix the ifdef to correctly check for $(BOARDDIR) rather
> than $(BOARD) since that's what is actually used in the include path.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Reviewed-by: Tom Rini <trini@konsulko.com>
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 5/6] mach-snapdragon: add kconfig infra for building MBN files
2026-05-11 13:57 ` [PATCH v7 5/6] mach-snapdragon: add kconfig infra for building MBN files Casey Connolly
@ 2026-06-08 14:28 ` Tom Rini
0 siblings, 0 replies; 11+ messages in thread
From: Tom Rini @ 2026-06-08 14:28 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, Sumit Garg, u-boot-qcom, Neil Armstrong,
Balaji Selvanathan, Varadarajan Narayanan, Quentin Schulz,
Heinrich Schuchardt, Marek Vasut, Peng Fan, Jaehoon Chung,
Aswin Murugan, Ilias Apalodimas, Michal Simek, David Lechner
[-- Attachment #1: Type: text/plain, Size: 1914 bytes --]
On Mon, May 11, 2026 at 03:57:47PM +0200, Casey Connolly wrote:
> Add a qualcomm specific Makefile fragment to make u-boot.mbn a build
> target and introduce a kconfig option to build it by default on relevant
> platforms via CONFIG_BUILD_TARGET.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> Kconfig | 1 +
> arch/arm/mach-snapdragon/Kconfig | 11 +++++++++++
> board/qualcomm/config.mk | 14 ++++++++++++++
> 3 files changed, 26 insertions(+)
>
> diff --git a/Kconfig b/Kconfig
> index 8428d039a1c0..183c8096030d 100644
> --- a/Kconfig
> +++ b/Kconfig
> @@ -557,8 +557,9 @@ config BUILD_TARGET
> default "u-boot-with-spl.sfp" if ARCH_SOCFPGA_GEN5
> default "u-boot.itb" if !BINMAN && SPL_LOAD_FIT && (ARCH_ROCKCHIP || \
> RISCV || ARCH_ZYNQMP)
> default "u-boot.kwb" if (ARCH_KIRKWOOD || ARMADA_32BIT) && !SPL
> + default "u-boot.mbn" if ARCH_SNAPDRAGON && QCOM_GENERATE_MBN
> help
> Some SoCs need special image types (e.g. U-Boot binary
> with a special header) as build targets. By defining
> CONFIG_BUILD_TARGET in the SoC / board header, this
> diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig
> index d3de8693b5a8..6329cbb9215e 100644
> --- a/arch/arm/mach-snapdragon/Kconfig
> +++ b/arch/arm/mach-snapdragon/Kconfig
> @@ -41,5 +41,16 @@ config SYS_CONFIG_NAME
> This option contains information about board configuration name.
> Based on this option include/configs/<CONFIG_SYS_CONFIG_NAME>.h header
> will be used for board configuration.
>
> +config QCOM_GENERATE_MBN
> + bool "Generate an MBN-compatible ELF binary"
> + default n
The default is always "n" so we only need default n if ... statements
when we would otherwise default y. There's other examples to clean up,
so only fix this if there's a v8. Thanks.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
` (5 preceding siblings ...)
2026-05-11 13:57 ` [PATCH v7 6/6] configs: qcom: use mkmbn and stop building ELF files Casey Connolly
@ 2026-07-20 13:10 ` Casey Connolly
6 siblings, 0 replies; 11+ messages in thread
From: Casey Connolly @ 2026-07-20 13:10 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom, Casey Connolly
Cc: Tom Rini, Neil Armstrong, Balaji Selvanathan,
Varadarajan Narayanan, Quentin Schulz, Heinrich Schuchardt,
Marek Vasut, Peng Fan, Jaehoon Chung, Aswin Murugan,
Ilias Apalodimas, Michal Simek, David Lechner
On Mon, 11 May 2026 15:57:42 +0200, Casey Connolly wrote:
> Currently the build process for flashing U-Boot as the first-stage
> bootloader on Qualcomm platforms requires a manual step of test-signing
> the emitted ELF binary with the appropriate settings for it to be
> properly accepted by the pre-loader.
>
> To address this, let's introduce a new tool "mkmbn.py", this is based
> on the existing qtestsign[1] tool but introduces lookup table that maps
> from a boards DT compatible property to the load address it needs.
>
> [...]
Applied, thanks!
[1/6] config.mk: support vendor generic includes
(no commit info)
[2/6] tools: qcom: introduce mkmbn library
(no commit info)
[3/6] tools: qcom: add mkmbn.py
(no commit info)
[4/6] doc: board/qualcomm: update docs for new u-boot.mbn target
(no commit info)
[5/6] mach-snapdragon: add kconfig infra for building MBN files
(no commit info)
[6/6] configs: qcom: use mkmbn and stop building ELF files
(no commit info)
Best regards,
--
// Casey (she/they)
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-07-20 13:10 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-11 13:57 [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
2026-05-11 13:57 ` [PATCH v7 1/6] config.mk: support vendor generic includes Casey Connolly
2026-06-08 14:27 ` Tom Rini
2026-05-11 13:57 ` [PATCH v7 2/6] tools: qcom: introduce mkmbn library Casey Connolly
2026-05-11 13:57 ` [PATCH v7 3/6] tools: qcom: add mkmbn.py Casey Connolly
2026-05-11 13:57 ` [PATCH v7 4/6] doc: board/qualcomm: update docs for new u-boot.mbn target Casey Connolly
2026-05-11 13:57 ` [PATCH v7 5/6] mach-snapdragon: add kconfig infra for building MBN files Casey Connolly
2026-06-08 14:28 ` Tom Rini
2026-05-11 13:57 ` [PATCH v7 6/6] configs: qcom: use mkmbn and stop building ELF files Casey Connolly
2026-05-21 12:23 ` Stephan Gerhold
2026-07-20 13:10 ` [PATCH v7 0/6] Qualcomm: teach the build system to emit signed ELF images Casey Connolly
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox