* [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images
@ 2025-06-02 16:10 Casey Connolly
2025-06-02 16:10 ` [PATCH v2 1/7] binman: add $(srctree)/tools to toolpath Casey Connolly
` (7 more replies)
0 siblings, 8 replies; 14+ messages in thread
From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw)
To: Tom Rini, Neil Armstrong, Sumit Garg
Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan,
Aswin Murugan, Casey Connolly
With several new Qualcomm platforms appearing on the mailing list, all
of which build U-Boot as an ELF, sign it, and then flash it to some
partition on the board, we're getting a lot of defconfigs which just
contain a debug UART and a TEXT_BASE address. This leads to needless
rebuilds in CI of effectively the same image, and needless duplication
of build instructions.
To address this, let's introduce a new tool "mkmbn.py", this is based
on the existing qtestsign[1] tool but is (currently) hardcoded to
only handle the Dragonwing and IPQ boards which use MBN header v6.
Additionally, the tool contains a lookup table that maps from a boards
DT compatible property to the load address it needs. Since it's highly
unusual for different boards using the same SoC to have different load
addresses, generic platform matching is also done (e.g. "qcom,qcm6490").
With this tool in place, we teach binman to use it and introduce
a qcom-binman.dts file to describe the image. The tool will emit a
u-boot.mbn file for supported platforms, for unsupported platforms the
u-boot MBN etype in binman will print a notice explaining that no mbn
file is built and to look at tools/qcom/mkmbn/mkmbn.py
Finally, the defconfigs are cleaned up by moving the debug UART
definitions to config fragments (since it doesn't make sense to have
them enabled by default anyway) and remove CONFIG_REMAKE_ELF. Notably,
the qcs9100_defconfig is removed entirely since the same binary can
be created with just make arguments. This platform entirely lacked
documentation to begin with, which should be addressed by future
patches.
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.
These patches have been tested on the RB3 Gen 2, but additional
testing for other platforms would be super useful.
[1]: https://github.com/msm8916-mainline/qtestsign
To: Tom Rini <trini@konsulko.com>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Sumit Garg <sumit.garg@kernel.org>
Cc: u-boot@lists.denx.de
Cc: u-boot-qcom@groups.io
Cc: Varadarajan Narayanan <quic_varada@quicinc.com>
Cc: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
Cc: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
Signed-off-by: Casey Connolly <casey.connolly@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
---
Casey Connolly (7):
binman: add $(srctree)/tools to toolpath
binman: support building binman dtb when OF_UPSTREAM is enabled
tools: add mkmbn tool for Qualcomm
binman: add support for building Qualcomm signed MBN ELF images
configs: qualcomm: use fragments for debug UART
configs: qualcomm: use mkmbn and stop creating ELF files
doc: board/qualcomm: update docs for new u-boot.mbn target
Makefile | 5 +
board/qualcomm/debug-qcm6490.config | 5 +
board/qualcomm/debug-qcs9100.config | 5 +
configs/qcm6490_defconfig | 10 --
configs/qcom_ipq9574_mmc_defconfig | 1 -
configs/qcs9100_defconfig | 18 ---
doc/board/qualcomm/index.rst | 1 +
doc/board/qualcomm/rb3gen2.rst | 27 ++--
doc/board/qualcomm/rdp.rst | 16 +-
doc/board/qualcomm/signing.rst | 29 ++++
dts/Makefile | 19 ++-
tools/binman/btool/mkmbn.py | 29 ++++
tools/binman/etype/u_boot_mbn.py | 51 ++++++
tools/mkmbn | 1 +
tools/qcom/mkmbn/cert.py | 158 ++++++++++++++++++
tools/qcom/mkmbn/elf.py | 243 ++++++++++++++++++++++++++++
tools/qcom/mkmbn/hashseg.py | 308 ++++++++++++++++++++++++++++++++++++
tools/qcom/mkmbn/mkmbn.py | 154 ++++++++++++++++++
18 files changed, 1024 insertions(+), 56 deletions(-)
---
base-commit: b22a276f039f818d5564bec6637071cfc8a7e432
change-id: 20250522-b4-qcom-tooling-improvements-ab40585b11a1
Casey Connolly <casey.connolly@linaro.org>
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v2 1/7] binman: add $(srctree)/tools to toolpath 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-02 16:10 ` [PATCH v2 2/7] binman: support building binman dtb when OF_UPSTREAM is enabled Casey Connolly ` (6 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly Not all tools need building, look in the srctree toolpath as well as the objtree. Signed-off-by: Casey Connolly <casey.connolly@linaro.org> --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index f88684947ee55fbc28768eeae159432649cc9ec6..719f7f6ba5161f9ae08fcfeec21b45629ef4bb3e 100644 --- a/Makefile +++ b/Makefile @@ -1402,8 +1402,13 @@ else binman_dtb = ./u-boot.dtb endif endif +# Not all tools are in the output dir, use the srctree toolpath as well +ifneq ($(srctree),$(objtree)) +BINMAN_TOOLPATHS += $(srctree)/tools +endif + quiet_cmd_binman = BINMAN $@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ $(foreach f,$(BINMAN_TOOLPATHS),--toolpath $(f)) \ --toolpath $(objtree)/tools \ -- 2.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 2/7] binman: support building binman dtb when OF_UPSTREAM is enabled 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly 2025-06-02 16:10 ` [PATCH v2 1/7] binman: add $(srctree)/tools to toolpath Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-02 16:10 ` [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm Casey Connolly ` (5 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly Since binman DTB files are not upstream they are kept in the U-Boot dts dirs, this means they will not be built when building with OF_UPSTREAM enabled. Resolve this by adding a rule to build the binman DTB if both conditions are met. Signed-off-by: Casey Connolly <casey.connolly@linaro.org> --- dts/Makefile | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/dts/Makefile b/dts/Makefile index 86bf8dc215603c3c02930e9c642d7dc06ce715fc..c20d0fb7cca9d117030a882be783a8e855e10749 100644 --- a/dts/Makefile +++ b/dts/Makefile @@ -25,8 +25,15 @@ local_dtbos := local-dtbos else local_dtbos := endif +binman_dtb := $(subst ",,$(CONFIG_BINMAN_DTB)) +ifneq ($(binman_dtb),) +binman_dtb_target := binman-dtb +else +binman_dtb_target := +endif + ifneq ($(EXT_DTB),) DTB := $(EXT_DTB) else DTB := $(dt_dir)/$(DEVICE_TREE).dtb @@ -45,9 +52,9 @@ $(obj)/dt.dtb: $(DTB) FORCE endif targets += dt.dtb -$(DTB): arch-dtbs $(local_dtbos) +$(DTB): arch-dtbs $(local_dtbos) $(binman_dtb_target) $(Q)test -e $@ || ( \ echo >&2; \ echo >&2 "Device Tree Source ($@) is not correctly specified."; \ echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'"; \ @@ -64,8 +71,18 @@ PHONY += local-dtbos local-dtbos: $(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbos endif +# If CONFIG_BINMAN_DTB is set and CONFIG_OF_UPSTREAM is set then +# ensure we build the binman DTB as well +ifneq ($(binman_dtb),) +ifeq ($(CONFIG_OF_UPSTREAM),y) +PHONY += binman-dtb +binman-dtb: + $(MAKE) $(build)=arch/$(ARCH)/dts $(binman_dtb) +endif +endif + ifeq ($(CONFIG_XPL_BUILD),y) obj-$(CONFIG_OF_EMBED) := dt-spl.dtb.o # support "out-of-tree" build for dtb-spl $(obj)/dt-spl.dtb.o: $(obj)/dt-spl.dtb.S FORCE -- 2.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly 2025-06-02 16:10 ` [PATCH v2 1/7] binman: add $(srctree)/tools to toolpath Casey Connolly 2025-06-02 16:10 ` [PATCH v2 2/7] binman: support building binman dtb when OF_UPSTREAM is enabled Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-04 9:15 ` Stephan Gerhold 2025-06-02 16:10 ` [PATCH v2 4/7] binman: add support for building Qualcomm signed MBN ELF images Casey Connolly ` (4 subsequent siblings) 7 siblings, 1 reply; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly This is a fork of qtestsign[1] with modifications to integrate with the U-Boot build system. 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. Since different boards require different load addresses, the traditional CONFIG_REMAKE_ELF with CONFIG_TEXT_BASE requires introducing a new defconfig for each platform, even though the binary is otherwise identical. Since we already need to process the ELF file to produce a valid "MBN" which sbl1 will accept, mkmbn additionally inspects the U-Boot binary, finding the DTB and then checking for known compatible strings to identify the board or platform. With this, one can build U-Boot with qcom_defconfig: $ make DEVICE_TREE=qcom/qcs6490-rb3gen2 Then build an MBN with $ ./tools/qcom/mkmbn/mkmbn.py u-boot.bin The resulting u-boot.mbn will have the correct load address and can be directly flashed to the board from the bootROM with edl.py [1]: https://github.com/msm8916-mainline/qtestsign Signed-off-by: Casey Connolly <casey.connolly@linaro.org> --- tools/mkmbn | 1 + tools/qcom/mkmbn/cert.py | 158 +++++++++++++++++++++++ tools/qcom/mkmbn/elf.py | 243 ++++++++++++++++++++++++++++++++++ tools/qcom/mkmbn/hashseg.py | 308 ++++++++++++++++++++++++++++++++++++++++++++ tools/qcom/mkmbn/mkmbn.py | 154 ++++++++++++++++++++++ 5 files changed, 864 insertions(+) diff --git a/tools/mkmbn b/tools/mkmbn new file mode 120000 index 0000000000000000000000000000000000000000..a7b2096756f76c07ca21e73c63f3b8be28a4cf59 --- /dev/null +++ b/tools/mkmbn @@ -0,0 +1 @@ +qcom/mkmbn/mkmbn.py \ No newline at end of file diff --git a/tools/qcom/mkmbn/cert.py b/tools/qcom/mkmbn/cert.py new file mode 100644 index 0000000000000000000000000000000000000000..2fb195dce0d59f739bf40249e1fcf04e7546f315 --- /dev/null +++ b/tools/qcom/mkmbn/cert.py @@ -0,0 +1,158 @@ +# 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 0000000000000000000000000000000000000000..38e145b6f62bf482bcd8161c90dab569c525b288 --- /dev/null +++ b/tools/qcom/mkmbn/elf.py @@ -0,0 +1,243 @@ +# 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 + + # 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) + 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") + + @staticmethod + def parse(b: bytes, offset: int, ei_class: int) -> Phdr: + if ei_class == Ehdr.CLASS32: + unpack = 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) + + @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: + 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 __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 + 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 0000000000000000000000000000000000000000..f85504655a8c6396951f778a36a3ce384ca6f217 --- /dev/null +++ b/tools/qcom/mkmbn/hashseg.py @@ -0,0 +1,308 @@ +# 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) +# 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 + +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 +# +# 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. + +PHDR_FLAGS_HDR_PLACEHOLDER = 0x7000000 # placeholder for hash over ELF header +PHDR_FLAGS_HASH_SEGMENT = 0x2200000 # hash table segment + +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. +METADATA_SIZE = 120 + + +def _align(i: int, alignment: int) -> int: + mask = max(alignment - 1, 0) + return (i + mask) & ~mask + + +@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 + ) + + +HashSegment = { + 3: HashSegmentV3, + 5: HashSegmentV5, + 6: HashSegmentV6, +} + + +def drop(elff: elf.Elf): + # Drop existing hash segments + elff.phdrs = [ + phdr + for phdr in elff.phdrs + if phdr.p_type != 0 + or phdr.p_flags not in [PHDR_FLAGS_HASH_SEGMENT, PHDR_FLAGS_HDR_PLACEHOLDER] + ] + + +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" * 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( + _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 = _align( + max(phdr.p_paddr + phdr.p_memsz for phdr in elff.phdrs), HASH_SEG_ALIGN + ) + hash_seg.update(hash_addr) + # print(hash_seg) + + # Insert new hash NULL segment + hash_phdr = elf.Phdr( + 0, + HASH_SEG_ALIGN, + hash_addr, + hash_addr, + hash_seg.size_with_header, + _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(0, 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() diff --git a/tools/qcom/mkmbn/mkmbn.py b/tools/qcom/mkmbn/mkmbn.py new file mode 100755 index 0000000000000000000000000000000000000000..94d85275c19c01c1e0aa64a61b615d14c10c0468 --- /dev/null +++ b/tools/qcom/mkmbn/mkmbn.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2024 Stephan Gerhold +# Copyright (C) 2025 Casey Connolly +from __future__ import annotations + +import argparse +from pathlib import Path + +from elf import Elf, Phdr +import hashseg +import sys +from enum import Enum + +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 + 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.aboot), + b"qcom,qcs9100-ride-r3\0": MbnData(0xAF000000, 6, SwId.aboot), # Dragonwing IQ9 + b"qcom,qcs8300-ride\0": MbnData(0xAF000000, 6, SwId.aboot), # Dragonwing IQ8 + b"qcom,qcs615-ride\0": MbnData(0x9FC00000, 6, SwId.aboot), # 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.aboot), # rb3gen2, rubikpi3 + b"qcom,qcs9100\0": MbnData(0xAF000000, 6, SwId.aboot), # Dragonwing IQ9 + b"qcom,qcs8300\0": MbnData(0xAF000000, 6, SwId.aboot), # Dragonwing IQ8 + b"qcom,qcs615\0": MbnData(0x9FC00000, 6, SwId.aboot), # Dragonwing IQ6 + 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() + +elf = Elf() + +data: bytes = args.bin.read() + +# dtb is at the end, so find the last match +dtb_off = 0 +off = 0 +while True: + off = data.find(b"\xd0\x0d\xfe\xed", dtb_off + 1) + if off == -1: + break + 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}") + +mbn: MbnData|None = None + +for match, mbndata in boards.items(): + if data.find(match, dtb_off) != -1: + mbn = mbndata + break + +if not mbn: + error( + "Not building an MBN file for this board, see tools/qcom/mkmbn/mkmbn.py for details" + ) + # Bailing out would fail the build, and it's not possible to know if an MBN + # is actually needed for the board we're building for. Minimise confusion by removing + # any file that might exist from a previous build and exit with a known code. + args.output.unlink(missing_ok=True) + exit(61) + +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.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm 2025-06-02 16:10 ` [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm Casey Connolly @ 2025-06-04 9:15 ` Stephan Gerhold 2025-06-04 11:14 ` Casey Connolly 0 siblings, 1 reply; 14+ messages in thread From: Stephan Gerhold @ 2025-06-04 9:15 UTC (permalink / raw) To: Casey Connolly Cc: Tom Rini, Neil Armstrong, Sumit Garg, u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan On Mon, Jun 02, 2025 at 06:10:54PM +0200, Casey Connolly wrote: > This is a fork of qtestsign[1] with modifications to integrate with the > U-Boot build system. I think it would be good if the "library" part of qtestsign (cert.py, elf.py, hashseg.py) would stay unmodified if possible, and you only add a new frontend on top (mkmbn.py). This would simplify syncing upstream changes in the future, e.g. should the v7 support get introduced at some point. Have you changed anything meaningful inside these three files? Copying the original files back in I mostly see re-formatting at the moment, which makes it hard to see what is different. Thanks, Stephan ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm 2025-06-04 9:15 ` Stephan Gerhold @ 2025-06-04 11:14 ` Casey Connolly 0 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-04 11:14 UTC (permalink / raw) To: Stephan Gerhold Cc: Tom Rini, Neil Armstrong, Sumit Garg, u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan Hi Stephan On 6/4/25 11:15, Stephan Gerhold wrote: > On Mon, Jun 02, 2025 at 06:10:54PM +0200, Casey Connolly wrote: >> This is a fork of qtestsign[1] with modifications to integrate with the >> U-Boot build system. > > I think it would be good if the "library" part of qtestsign (cert.py, > elf.py, hashseg.py) would stay unmodified if possible, and you only add > a new frontend on top (mkmbn.py). This would simplify syncing upstream > changes in the future, e.g. should the v7 support get introduced at some > point. Ack, I thought about this but way too late...> > Have you changed anything meaningful inside these three files? Copying > the original files back in I mostly see re-formatting at the moment, > which makes it hard to see what is different. Yes, I made some changes so that I can construct an ELF header out of thin air, since the original code only lets you parse one from a file. Sucks about the formatting changes, not sure when that happened. I will open a PR to qtestsign adding the features I need so hopefully we can keep them aligned. > > Thanks, > StephanKind regards, -- Casey (she/they) ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 4/7] binman: add support for building Qualcomm signed MBN ELF images 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly ` (2 preceding siblings ...) 2025-06-02 16:10 ` [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-02 16:10 ` [PATCH v2 5/7] configs: qualcomm: use fragments for debug UART Casey Connolly ` (3 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly Implement support for building u-boot.mbn files using the new mkmbn tool. Signed-off-by: Casey Connolly <casey.connolly@linaro.org> --- tools/binman/btool/mkmbn.py | 29 +++++++++++++++++++++++ tools/binman/etype/u_boot_mbn.py | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/tools/binman/btool/mkmbn.py b/tools/binman/btool/mkmbn.py new file mode 100644 index 0000000000000000000000000000000000000000..232e2b442a166f9e86a4d4eb5f55f624eee0dc62 --- /dev/null +++ b/tools/binman/btool/mkmbn.py @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2025, Linaro Ltd. +# +"""Bintool implementation for mkmbn""" + +from binman import bintool +from u_boot_pylib.command import CommandResult + +class Bintoolmkmbn(bintool.Bintool): + """Qualcomm MBN generator for U-Boot + """ + def __init__(self, name): + super().__init__(name, 'Generate MBN (signed ELF) image for U-Boot') + + # pylint: disable=R0913 + def run(self, infile: str) -> CommandResult: + """Run mkmbn.py + """ + args = [infile] + return self.run_cmd_result(*args, raise_on_error=False) + + + def get_path(self): + path = super().get_path() + return path + + + def fetch(self, _method): + return True diff --git a/tools/binman/etype/u_boot_mbn.py b/tools/binman/etype/u_boot_mbn.py new file mode 100644 index 0000000000000000000000000000000000000000..35c271a45e3ce75f10d1852eb0ac290b38913d84 --- /dev/null +++ b/tools/binman/etype/u_boot_mbn.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2025 Linaro Ltd +# Authored by Casey Connolly <casey.connolly@linaro.org> +# +# Entry-type module for 'u-boot.mbn' +# + +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from u_boot_pylib import tout +from u_boot_pylib import tools +from binman.btool.mkmbn import Bintoolmkmbn + +""" +Casey TODO: + +* move qcom-binman.dts back to arch/arm/dts and figure out how to make it build... +* set CONFIG_BINMAN_DTB in qcom_defconfig +* update docs + +""" + +class Entry_u_boot_mbn(Entry_blob): + """U-Boot Qualcomm signed MBN ELF image + + This is an ELF file with custom program headers containing Qualcomm + specific hashes, signatures and other metadata. It contains the + u-boot.bin image. + """ + mkmbn: Bintoolmkmbn + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def ObtainContents(self): + self._pathname = tools.get_input_filename('u-boot.bin') + res = self.mkmbn.run(self._pathname) + if res is None: + self.Raise('mkmbn not found or failed!') + # Exit code 61 (ENODATA) indicates that an MBN file can't be built + # for this board. This is non-fatal since it may not be required. + # however we print the stderr which indicates how to add MBN support + # if it is actually needed for this platform. + if res.return_code == 61: + print(res.stderr, end='') + return self.ReadBlobContents() + + def GetDefaultFilename(self): + return 'u-boot.mbn' + + def AddBintools(self, btools): + self.mkmbn = self.AddBintool(btools, 'mkmbn') -- 2.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 5/7] configs: qualcomm: use fragments for debug UART 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly ` (3 preceding siblings ...) 2025-06-02 16:10 ` [PATCH v2 4/7] binman: add support for building Qualcomm signed MBN ELF images Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-02 16:10 ` [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files Casey Connolly ` (2 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly The QCM6490 and QCS9100 targets always enable debug UART, but this is not really optimal for typical users. Move these debug UART options to config fragments so that they aren't enabled by default. Signed-off-by: Casey Connolly <casey.connolly@linaro.org> --- board/qualcomm/debug-qcm6490.config | 5 +++++ board/qualcomm/debug-qcs9100.config | 5 +++++ configs/qcm6490_defconfig | 6 ------ configs/qcs9100_defconfig | 6 ------ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/board/qualcomm/debug-qcm6490.config b/board/qualcomm/debug-qcm6490.config new file mode 100644 index 0000000000000000000000000000000000000000..9ea54758794ef9d2f698cb4026da44be2360147a --- /dev/null +++ b/board/qualcomm/debug-qcm6490.config @@ -0,0 +1,5 @@ +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0x994000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=14745600 diff --git a/board/qualcomm/debug-qcs9100.config b/board/qualcomm/debug-qcs9100.config new file mode 100644 index 0000000000000000000000000000000000000000..8ca042b440d100e9583ff9504f1b0e67a491dfcf --- /dev/null +++ b/board/qualcomm/debug-qcs9100.config @@ -0,0 +1,5 @@ +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0xA8C000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=14745600 diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig index ba26924da161b1c4b5378955185f314b05cb1006..22afa7be1a944e5e0d712ef8aed21ea2b7fa2fa1 100644 --- a/configs/qcm6490_defconfig +++ b/configs/qcm6490_defconfig @@ -7,14 +7,8 @@ # Otherwise buildman thinks this isn't an ARM platform CONFIG_ARM=y -CONFIG_DEBUG_UART=y -CONFIG_DEBUG_UART_ANNOUNCE=y -CONFIG_DEBUG_UART_BASE=0x994000 -CONFIG_DEBUG_UART_MSM_GENI=y -CONFIG_DEBUG_UART_CLOCK=14745600 - # Address where U-Boot will be loaded CONFIG_TEXT_BASE=0x9fc00000 CONFIG_REMAKE_ELF=y diff --git a/configs/qcs9100_defconfig b/configs/qcs9100_defconfig index 10ff4d2539803143c8b770b6b28e8dc52b1d5846..4ceeba63ec12cebbc2124eb256a255450500152f 100644 --- a/configs/qcs9100_defconfig +++ b/configs/qcs9100_defconfig @@ -4,14 +4,8 @@ # a dedicated "uefi" partition for edk2/U-Boot) #include "qcom_defconfig" -CONFIG_DEBUG_UART=y -CONFIG_DEBUG_UART_ANNOUNCE=y -CONFIG_DEBUG_UART_BASE=0xA8C000 -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 -- 2.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly ` (4 preceding siblings ...) 2025-06-02 16:10 ` [PATCH v2 5/7] configs: qualcomm: use fragments for debug UART Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-04 9:21 ` Stephan Gerhold 2025-06-02 16:10 ` [PATCH v2 7/7] doc: board/qualcomm: update docs for new u-boot.mbn target Casey Connolly 2025-06-04 9:07 ` [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Stephan Gerhold 7 siblings, 1 reply; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly Remove qcs9100_defconfig since it is now identical to qcom_defconfig. Additionally remove the load address and REMAKE_ELF from other Qualcomm defconfigs. For these platforms you should explicitly build the u-boot.mbn target which will have the correct load address and Qualcomm MBN signatures. Signed-off-by: Casey Connolly <casey.connolly@linaro.org> --- configs/qcm6490_defconfig | 4 ---- configs/qcom_ipq9574_mmc_defconfig | 1 - configs/qcs9100_defconfig | 12 ------------ 3 files changed, 17 deletions(-) diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig index 22afa7be1a944e5e0d712ef8aed21ea2b7fa2fa1..b9d0ca113a798582a83cccd3aa19c2a94b771b64 100644 --- a/configs/qcm6490_defconfig +++ b/configs/qcm6490_defconfig @@ -7,12 +7,8 @@ # Otherwise buildman thinks this isn't an ARM platform CONFIG_ARM=y -# Address where U-Boot will be loaded -CONFIG_TEXT_BASE=0x9fc00000 -CONFIG_REMAKE_ELF=y - CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs6490-rb3gen2" # Enable capsule updates CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y diff --git a/configs/qcom_ipq9574_mmc_defconfig b/configs/qcom_ipq9574_mmc_defconfig index 8d7d3d92c73a30b246acc2eae6da1d7b04892691..00abc8430596dadd574d5d2b7353ecfdef01e2b4 100644 --- a/configs/qcom_ipq9574_mmc_defconfig +++ b/configs/qcom_ipq9574_mmc_defconfig @@ -11,9 +11,8 @@ 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_EFI_LOADER is not set CONFIG_FIT=y CONFIG_FIT_VERBOSE=y # CONFIG_BOOTSTD is not set diff --git a/configs/qcs9100_defconfig b/configs/qcs9100_defconfig deleted file mode 100644 index 4ceeba63ec12cebbc2124eb256a255450500152f..0000000000000000000000000000000000000000 --- a/configs/qcs9100_defconfig +++ /dev/null @@ -1,12 +0,0 @@ -# Configuration for building U-Boot to be flashed -# to the uefi partition of QCS9100 based dev boards with -# the "Linux Embedded" partition layout (which have -# a dedicated "uefi" partition for edk2/U-Boot) - -#include "qcom_defconfig" - -# Address where U-Boot will be loaded -CONFIG_TEXT_BASE=0xaf000000 -CONFIG_REMAKE_ELF=y - -CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs9100-ride-r3" -- 2.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files 2025-06-02 16:10 ` [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files Casey Connolly @ 2025-06-04 9:21 ` Stephan Gerhold 2025-06-04 11:17 ` Casey Connolly 0 siblings, 1 reply; 14+ messages in thread From: Stephan Gerhold @ 2025-06-04 9:21 UTC (permalink / raw) To: Casey Connolly Cc: Tom Rini, Neil Armstrong, Sumit Garg, u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan On Mon, Jun 02, 2025 at 06:10:57PM +0200, Casey Connolly wrote: > Remove qcs9100_defconfig since it is now identical to qcom_defconfig. > Additionally remove the load address and REMAKE_ELF from other Qualcomm > defconfigs. > > For these platforms you should explicitly build the u-boot.mbn target > which will have the correct load address and Qualcomm MBN signatures. What is the motivation to require explicitly building the u-boot.mbn target? Couldn't we do that automatically like before with REMAKE_ELF (i.e. add u-boot.mbn to INPUTS-y)? Or is this because of the additional Python dependencies (the cryptography module)? BTW: We should probably document the Python "cryptography" requirement somewhere so people don't get confused? qtestsign is Python 3.7+ and cryptography 3.1+. Thanks, Stephan ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files 2025-06-04 9:21 ` Stephan Gerhold @ 2025-06-04 11:17 ` Casey Connolly 0 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-04 11:17 UTC (permalink / raw) To: Stephan Gerhold Cc: Tom Rini, Neil Armstrong, Sumit Garg, u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan On 6/4/25 11:21, Stephan Gerhold wrote: > On Mon, Jun 02, 2025 at 06:10:57PM +0200, Casey Connolly wrote: >> Remove qcs9100_defconfig since it is now identical to qcom_defconfig. >> Additionally remove the load address and REMAKE_ELF from other Qualcomm >> defconfigs. >> >> For these platforms you should explicitly build the u-boot.mbn target >> which will have the correct load address and Qualcomm MBN signatures. > > What is the motivation to require explicitly building the u-boot.mbn > target? Couldn't we do that automatically like before with REMAKE_ELF > (i.e. add u-boot.mbn to INPUTS-y)? Or is this because of the additional > Python dependencies (the cryptography module)? Oops, the wording here needs updating. This was how it worked in v1, but v2 uses binman and spits out the u-boot.mbn automagically when building with qcom_defconfig for a target that mkmbn.py recognises. > > BTW: We should probably document the Python "cryptography" requirement > somewhere so people don't get confused? qtestsign is Python 3.7+ and > cryptography 3.1+. > > Thanks, > Stephan -- Casey (she/they) ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 7/7] doc: board/qualcomm: update docs for new u-boot.mbn target 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly ` (5 preceding siblings ...) 2025-06-02 16:10 ` [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files Casey Connolly @ 2025-06-02 16:10 ` Casey Connolly 2025-06-04 9:07 ` [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Stephan Gerhold 7 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-02 16:10 UTC (permalink / raw) To: Tom Rini, Neil Armstrong, Sumit Garg Cc: u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan, Casey Connolly 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/index.rst | 1 + doc/board/qualcomm/rb3gen2.rst | 27 +++++++++++++-------------- doc/board/qualcomm/rdp.rst | 16 ++++------------ doc/board/qualcomm/signing.rst | 29 +++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/doc/board/qualcomm/index.rst b/doc/board/qualcomm/index.rst index e2fcbfa19c2fff36c8551823ca4a53a5fdc7fa9a..0b24fb6691907ab6fcecc6ade55f363de44502a8 100644 --- a/doc/board/qualcomm/index.rst +++ b/doc/board/qualcomm/index.rst @@ -11,4 +11,5 @@ Qualcomm board phones debugging rdp + signing diff --git a/doc/board/qualcomm/rb3gen2.rst b/doc/board/qualcomm/rb3gen2.rst index 4240606224f5b36968277792dfd5dc93a762fdfa..79a57c689657719b61582fc33cd079f134cda054 100644 --- a/doc/board/qualcomm/rb3gen2.rst +++ b/doc/board/qualcomm/rb3gen2.rst @@ -17,34 +17,33 @@ 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 u-boot.mbn -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 +the firmware still expects firmware ELF images to be "signed". More information can be +found in :doc:`signing`. 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 fd14f1d9829dfaea4a7b503c1f60de8441f362e5..51f8d5772d054f791274f494ba3b7b0e1b691706 100644 --- a/doc/board/qualcomm/rdp.rst +++ b/doc/board/qualcomm/rdp.rst @@ -16,22 +16,14 @@ 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`. -Although the RDPs do 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 - -Then install the resulting ``u-boot.mbn`` to the ``0:APPSBL`` partition -on your device with:: +Then install the image to the ``0:APPSBL`` partition on your device with:: IPQ9574# tftpboot path/to/u-boot.mbn IPQ9574# mmc part (note down the start & end block no.s of '0:APPSBL' partition) IPQ9574# mmc erase <start blk no> <count> diff --git a/doc/board/qualcomm/signing.rst b/doc/board/qualcomm/signing.rst new file mode 100644 index 0000000000000000000000000000000000000000..317cd57cefeeb3b073ebec79286f00414d1da342 --- /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.49.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly ` (6 preceding siblings ...) 2025-06-02 16:10 ` [PATCH v2 7/7] doc: board/qualcomm: update docs for new u-boot.mbn target Casey Connolly @ 2025-06-04 9:07 ` Stephan Gerhold 2025-06-04 11:18 ` Casey Connolly 7 siblings, 1 reply; 14+ messages in thread From: Stephan Gerhold @ 2025-06-04 9:07 UTC (permalink / raw) To: Casey Connolly Cc: Tom Rini, Neil Armstrong, Sumit Garg, u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan On Mon, Jun 02, 2025 at 06:10:51PM +0200, Casey Connolly wrote: > With several new Qualcomm platforms appearing on the mailing list, all > of which build U-Boot as an ELF, sign it, and then flash it to some > partition on the board, we're getting a lot of defconfigs which just > contain a debug UART and a TEXT_BASE address. This leads to needless > rebuilds in CI of effectively the same image, and needless duplication > of build instructions. > > To address this, let's introduce a new tool "mkmbn.py", this is based > on the existing qtestsign[1] tool but is (currently) hardcoded to > only handle the Dragonwing and IPQ boards which use MBN header v6. It doesn't feel right to limit the use of this tool to v6 boards, given that qtestsign supports v3 boards such as the DragonBoard 410c as well, which is also using the CONFIG_REMAKE_ELF functionality right now. Which changes are needed to make this work for DB410c as well? Looking more at the diff in v2, I think the hardcoding of v6 is actually already gone(?), since there are entries for qcom,apq8016/qcom,msm8916 now. What else is needed to use this for DB410c? I'm happy to test on DB410c if needed, just ping me in the chat. Thanks, Stephan ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images 2025-06-04 9:07 ` [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Stephan Gerhold @ 2025-06-04 11:18 ` Casey Connolly 0 siblings, 0 replies; 14+ messages in thread From: Casey Connolly @ 2025-06-04 11:18 UTC (permalink / raw) To: Stephan Gerhold Cc: Tom Rini, Neil Armstrong, Sumit Garg, u-boot, u-boot-qcom, Varadarajan Narayanan, Balaji Selvanathan, Aswin Murugan On 6/4/25 11:07, Stephan Gerhold wrote: > On Mon, Jun 02, 2025 at 06:10:51PM +0200, Casey Connolly wrote: >> With several new Qualcomm platforms appearing on the mailing list, all >> of which build U-Boot as an ELF, sign it, and then flash it to some >> partition on the board, we're getting a lot of defconfigs which just >> contain a debug UART and a TEXT_BASE address. This leads to needless >> rebuilds in CI of effectively the same image, and needless duplication >> of build instructions. >> >> To address this, let's introduce a new tool "mkmbn.py", this is based >> on the existing qtestsign[1] tool but is (currently) hardcoded to >> only handle the Dragonwing and IPQ boards which use MBN header v6. > > It doesn't feel right to limit the use of this tool to v6 boards, given > that qtestsign supports v3 boards such as the DragonBoard 410c as well, > which is also using the CONFIG_REMAKE_ELF functionality right now. > > Which changes are needed to make this work for DB410c as well? Looking > more at the diff in v2, I think the hardcoding of v6 is actually already > gone(?), since there are entries for qcom,apq8016/qcom,msm8916 now. What > else is needed to use this for DB410c? Ah, another place I forgot to fix the wording... This should already work on db410c it just needs testing :)> > I'm happy to test on DB410c if needed, just ping me in the chat. > > Thanks, > Stephan -- Casey (she/they) ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-06-04 11:18 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-06-02 16:10 [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Casey Connolly 2025-06-02 16:10 ` [PATCH v2 1/7] binman: add $(srctree)/tools to toolpath Casey Connolly 2025-06-02 16:10 ` [PATCH v2 2/7] binman: support building binman dtb when OF_UPSTREAM is enabled Casey Connolly 2025-06-02 16:10 ` [PATCH v2 3/7] tools: add mkmbn tool for Qualcomm Casey Connolly 2025-06-04 9:15 ` Stephan Gerhold 2025-06-04 11:14 ` Casey Connolly 2025-06-02 16:10 ` [PATCH v2 4/7] binman: add support for building Qualcomm signed MBN ELF images Casey Connolly 2025-06-02 16:10 ` [PATCH v2 5/7] configs: qualcomm: use fragments for debug UART Casey Connolly 2025-06-02 16:10 ` [PATCH v2 6/7] configs: qualcomm: use mkmbn and stop creating ELF files Casey Connolly 2025-06-04 9:21 ` Stephan Gerhold 2025-06-04 11:17 ` Casey Connolly 2025-06-02 16:10 ` [PATCH v2 7/7] doc: board/qualcomm: update docs for new u-boot.mbn target Casey Connolly 2025-06-04 9:07 ` [PATCH v2 0/7] Qualcomm: teach the build system to emit signed ELF images Stephan Gerhold 2025-06-04 11:18 ` Casey Connolly
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.