{"id":2233066,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2233066/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/patch/20260505-b4-qcom-tooling-improvements-v6-2-a5fb673f4af6@linaro.org/","project":{"id":18,"url":"http://patchwork.ozlabs.org/api/1.2/projects/18/?format=json","name":"U-Boot","link_name":"uboot","list_id":"u-boot.lists.denx.de","list_email":"u-boot@lists.denx.de","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260505-b4-qcom-tooling-improvements-v6-2-a5fb673f4af6@linaro.org>","list_archive_url":null,"date":"2026-05-05T15:48:42","name":"[v6,2/6] tools: qcom: introduce mkmbn library","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"0ae3093e680eb7814d7d221beddd4f4d8a560db8","submitter":{"id":90679,"url":"http://patchwork.ozlabs.org/api/1.2/people/90679/?format=json","name":"Casey Connolly","email":"casey.connolly@linaro.org"},"delegate":{"id":151538,"url":"http://patchwork.ozlabs.org/api/1.2/users/151538/?format=json","username":"kcxt","first_name":"Casey","last_name":"Connolly","email":"casey.connolly@linaro.org"},"mbox":"http://patchwork.ozlabs.org/project/uboot/patch/20260505-b4-qcom-tooling-improvements-v6-2-a5fb673f4af6@linaro.org/mbox/","series":[{"id":502858,"url":"http://patchwork.ozlabs.org/api/1.2/series/502858/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/list/?series=502858","date":"2026-05-05T15:48:40","name":"Qualcomm: teach the build system to emit signed ELF images","version":6,"mbox":"http://patchwork.ozlabs.org/series/502858/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2233066/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2233066/checks/","tags":{},"related":[],"headers":{"Return-Path":"<u-boot-bounces@lists.denx.de>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=iGCyZdy6;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=85.214.62.61; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)","phobos.denx.de;\n dmarc=pass (p=none dis=none) header.from=linaro.org","phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de","phobos.denx.de;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=linaro.org header.i=@linaro.org header.b=\"iGCyZdy6\";\n\tdkim-atps=neutral","phobos.denx.de;\n dmarc=pass (p=none dis=none) header.from=linaro.org","phobos.denx.de;\n spf=pass smtp.mailfrom=casey.connolly@linaro.org"],"Received":["from phobos.denx.de (phobos.denx.de [85.214.62.61])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g92xh06vZz1yJx\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 06 May 2026 01:49:11 +1000 (AEST)","from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id 401068495E;\n\tTue,  5 May 2026 17:48:56 +0200 (CEST)","by phobos.denx.de (Postfix, from userid 109)\n id B0D8284986; Tue,  5 May 2026 17:48:54 +0200 (CEST)","from mail-wm1-x331.google.com (mail-wm1-x331.google.com\n [IPv6:2a00:1450:4864:20::331])\n (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id 88C6984951\n for <u-boot@lists.denx.de>; Tue,  5 May 2026 17:48:51 +0200 (CEST)","by mail-wm1-x331.google.com with SMTP id\n 5b1f17b1804b1-48984d29fe3so78634575e9.0\n for <u-boot@lists.denx.de>; Tue, 05 May 2026 08:48:51 -0700 (PDT)","from lion.localdomain (p4fc3dd86.dip0.t-ipconnect.de.\n [79.195.221.134]) by smtp.gmail.com with ESMTPSA id\n 5b1f17b1804b1-48d182ee923sm20305955e9.27.2026.05.05.08.48.49\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 05 May 2026 08:48:50 -0700 (PDT)"],"X-Spam-Checker-Version":"SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de","X-Spam-Level":"","X-Spam-Status":"No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED,\n DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED,\n SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=linaro.org; s=google; t=1777996131; x=1778600931; darn=lists.denx.de;\n h=cc:to:in-reply-to:references:message-id:content-transfer-encoding\n :mime-version:subject:date:from:from:to:cc:subject:date:message-id\n :reply-to; bh=8Ye926JFUbhuVwp3RI+0h4C5N+cOdwbmgzp5yRxmbYM=;\n b=iGCyZdy6cR7Ag2ImwTD7F0Qs5wkACLOBAbBsl4uWbNt9uI6afJ73mI8Gt9q7ADBfHe\n /Rsp9wIxhxddSdFPa8WVanh44g/uhRGyZ8u2bxovWY1NuiiC4Z8dcdGOnpESlQi73hwh\n 2MANsv0LrpfsxYfN0Dx780kkU1MFpgPCQnD6Kx32gZzZwC7tq5yPkqIKy0aZmCzCP/RS\n m9yUW+kOKo4fq//gx01+S3zdErhRYnCrCCL+fvxMnviMHG+tmidtfwsBCEp3gSeCESH7\n V2cENRg9cuadmpPFWLUY8x6OxG2UPekgi7yUnJQZffjineLilOGBgNE1KGuohqhtAXjm\n TQvg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777996131; x=1778600931;\n h=cc:to:in-reply-to:references:message-id:content-transfer-encoding\n :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to\n :cc:subject:date:message-id:reply-to;\n bh=8Ye926JFUbhuVwp3RI+0h4C5N+cOdwbmgzp5yRxmbYM=;\n b=TOwXtXxqk6BE+w971D7K02yCD9n/5gUCgz2w2GVuM6KKocGQDXhIEX4nhUuFmb0PgG\n 304hvpTeH93wDi0KXlWuZWKUON29G0aIxGLCs4WTe7GPd/JLWRHdG1aTFioP8+nS/87S\n Mf0EnO3yKWRf3hrYGfbdyi/NE7XtKwwM4tykQY9RVpBSGgz5qWfMU88umcN6UBBk20zY\n lIoOxCBrRkYmF6xI1BW2bu1i+1o1q3WUB9t/HM8KPIsYhWOeNfFkqycNtSXdmP17UH7g\n qH0QlSXTCuzH+XIRdpwa6F06uAxlesvwiNrFVaEjuVuugwiXwHFA1iOWkx7I2GCI4HPS\n im1Q==","X-Gm-Message-State":"AOJu0Yz/XwnBL6bABNbV51HWHBDqtzKZCoN28D3CY1Rkt33X54LaUTcP\n RdxoEmnO7i9EaWmf9ieIKpQjcplgKqSYYlp0Ut94RjOClVF7OtmQFwherxfoYyrR1RhUOkW80b4\n 1ORBGLRU=","X-Gm-Gg":"AeBDietvJHjou67MX59T/A5rXrZ8kuzoKxJxiuoxIxZzKmimvdtO/A2v26/sBWsEQhn\n 1SnowUL88UI7NnBz35pync0gGKwBBt2bSDd2GAmP9ztIw5gGa07JoO6kn5LDgK1Y5yg1jjGE9dH\n Ihu5wQ84ZqNUC0QkJAmTOEPYKcyCpdk67fNhlrHBzCHfb30bimzjP7IDaw1RBUVoEmit52LbxhN\n pplBVC8RoL7SzVLDNoKAYldaL2rdvLaS86X7OcXPTxvjU+7ik19dNq544QTlqFc9MltfcuUpSlL\n xcboISaGrnfDnCAop581+5otujaCT01/g2EqkHxUvahBfBAdWHgGq7vX8XWYHr6DfwgwU/MtpUF\n Ud2PhpcezQ8d9WR1rpmTAAUjEsdCD960Cj7ovHHhVRHYlXao0ad9SXqyGjbR1EXLR8/a0jiyhZk\n QwpNeF9D/YeAWMsSI+vxGdF8/yNb4ulxUmpCkaxFZLVcEPpJWfrFFayU5qaiYCYK+kotXdI/e5R\n 3aTCZs=","X-Received":"by 2002:a05:600c:4f42:b0:488:9fb7:376d with SMTP id\n 5b1f17b1804b1-48d18ceb2f4mr63441775e9.28.1777996130769;\n Tue, 05 May 2026 08:48:50 -0700 (PDT)","From":"Casey Connolly <casey.connolly@linaro.org>","Date":"Tue, 05 May 2026 17:48:42 +0200","Subject":"[PATCH v6 2/6] tools: qcom: introduce mkmbn library","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"\n <20260505-b4-qcom-tooling-improvements-v6-2-a5fb673f4af6@linaro.org>","References":"\n <20260505-b4-qcom-tooling-improvements-v6-0-a5fb673f4af6@linaro.org>","In-Reply-To":"\n <20260505-b4-qcom-tooling-improvements-v6-0-a5fb673f4af6@linaro.org>","To":"u-boot@lists.denx.de, Sumit Garg <sumit.garg@kernel.org>,\n u-boot-qcom@groups.io","Cc":"Tom Rini <trini@konsulko.com>,\n Casey Connolly <casey.connolly@linaro.org>,\n Neil Armstrong <neil.armstrong@linaro.org>,\n Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>,\n Varadarajan Narayanan <quic_varada@quicinc.com>,\n Quentin Schulz <quentin.schulz@cherry.de>,\n Heinrich Schuchardt <xypron.glpk@gmx.de>,\n Marek Vasut <marek.vasut+renesas@mailbox.org>, Peng Fan <peng.fan@nxp.com>,\n Jaehoon Chung <jh80.chung@samsung.com>,\n Aswin Murugan <aswin.murugan@oss.qualcomm.com>,\n Ilias Apalodimas <ilias.apalodimas@linaro.org>,\n Michal Simek <michal.simek@amd.com>, David Lechner <dlechner@baylibre.com>","X-Mailer":"b4 0.16-dev","X-Developer-Signature":"v=1; a=openpgp-sha256; l=28307;\n i=casey.connolly@linaro.org; h=from:subject:message-id;\n bh=D1aUE7dw5rKpJGJivrwMOiGNWpAPqkEdMuV2fvAoPzI=;\n b=owGbwMvMwCFYaeA6f6eBkTjjabUkhsxfgjHy3wJU/LijgrPmKx9iDFkauKtEUL7q+Ezz+K85z\n NOWihZ1lLIwCHIwyIopsoifWGbZtPayvcb2BRdg5rAygQxh4OIUgInkvmBkmCt4ZueJpI7nvldY\n XCzsQk8c/36Q6zejT4P0i3Tfi+fS+Rn+aUS4//A5xX5V59iJ3ULNe2ZP+c6sJ7zoGE/VpktxXFO\n mKgMA","X-Developer-Key":"i=casey.connolly@linaro.org; a=openpgp;\n fpr=83B24DA7FE145076BC38BB250CD904EB673A7C47","X-BeenThere":"u-boot@lists.denx.de","X-Mailman-Version":"2.1.39","Precedence":"list","List-Id":"U-Boot discussion <u-boot.lists.denx.de>","List-Unsubscribe":"<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>","List-Archive":"<https://lists.denx.de/pipermail/u-boot/>","List-Post":"<mailto:u-boot@lists.denx.de>","List-Help":"<mailto:u-boot-request@lists.denx.de?subject=help>","List-Subscribe":"<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>","Errors-To":"u-boot-bounces@lists.denx.de","Sender":"\"U-Boot\" <u-boot-bounces@lists.denx.de>","X-Virus-Scanned":"clamav-milter 0.103.8 at phobos.denx.de","X-Virus-Status":"Clean"},"content":"This is a fork of qtestsign[1] with modifications to integrate with the\nU-Boot build system. It is pulled from\n\nf3df53a5f0e3 (\"Rename \"fw\" to \"mbn\"\")\n\nNew Qualcomm dev boards flash U-Boot to the \"uefi\" partition, the format\nis a standard ELF file with custom program headers containing Qualcomm\nsignatures, hashes and other metadata. Currently this is accomplished\nwith qtestsign manually, let's instead import it so we can integrate it\ninto the build process.\n\nThis library will be used by a new mkmbn.py tool to create MBN files\nwhich can be directly flashed to the board.\n\n[1]: https://github.com/msm8916-mainline/qtestsign\n\nSigned-off-by: Casey Connolly <casey.connolly@linaro.org>\n---\n tools/qcom/mkmbn/cert.py    | 127 ++++++++++++++++\n tools/qcom/mkmbn/elf.py     | 205 +++++++++++++++++++++++++\n tools/qcom/mkmbn/hashseg.py | 356 ++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 688 insertions(+)","diff":"diff --git a/tools/qcom/mkmbn/cert.py b/tools/qcom/mkmbn/cert.py\nnew file mode 100644\nindex 000000000000..e14f88746d53\n--- /dev/null\n+++ b/tools/qcom/mkmbn/cert.py\n@@ -0,0 +1,127 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+# Copyright (C) 2021-2022 Stephan Gerhold\n+# See https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf\n+# Somewhat based on code snippets from https://cryptography.io/en/latest/x509/tutorial.html\n+from __future__ import annotations\n+\n+from datetime import datetime\n+from typing import List\n+\n+from cryptography import x509\n+from cryptography.hazmat.primitives import hashes, serialization\n+from cryptography.x509.oid import NameOID\n+\n+# NOTE: The certificate chain generated by qtestsign is NOT meant\n+# to be secure. The private keys are listed here to make the\n+# resulting files reproducible. THESE KEYS SHOULD ONLY BE USED\n+# FOR TESTING AND NOT FOR A PROPER SECURE BOOT SETUP.\n+\n+ROOT_KEY = serialization.load_pem_private_key(b\"\"\"\n+-----BEGIN PRIVATE KEY-----\n+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCjZqF/BwggY4Rs\n+Q1/wSNPLEKQEROZ9i/d+7CXZCukWph+SKHlv652oiAp+TgzIGQQXDlaA+qUoXUjp\n+g2KTmoulfQjrgc5CSCk6yA01VxNBqR81JorJx8aD9ApOFVoERlmWhZcR3B/LsVyd\n+vYgwFNNkqUh7fyywyy1Z1ijk4SyJVak1VxfdkTTeb1wr5Awjvh82PrdRQfOvctFH\n+mVITqdMckdRD3Sx7y8EvypAYpAUiWklNgditetXFjMoV6XyXTPCRkH9zzskrXP6i\n+neCyS7xUfEYPYNpabzhpdvkx9Is2PlCJA1fZ1ERZsWcag5vDZa3SHslH5Kh9+ssH\n+ps0Ul1j5AgMBAAECggEAHUEzOdBy+oWGwHFhnF4VmT4t91u0npawJYe3EQBckgMF\n+FQBtGYYoMHPG2S01KaAc9NnK0AXQCwWEl9Y/kGizhtn3fl67pG9R/mWxw7KGzpMu\n+dLAlWhIL7zUCoU8+UhScVpAtZ3OvN6NWDyHPX7hizptmUEIJKM//mx12LeBIvn+P\n+8tSiBXxoDGl0JZ+QMzmshOUXLLnxKITgBGL+G9A1qTZHIs6VV7HWH1ptfObulBZf\n+yEBK1YBzI6GnBGzLOWnZqGsSbQ717SObQo5rCoRDZB7z4bXNWDEvuH+rqzcs5liu\n+af4gmBHNOLGh+Ta5HJ0XeoqU5ANOWlUi95/n2dJufwKBgQDNaMlT1937SHPv/eBq\n+Be3MobllTx4vMYh6CtfP8QozTE+sTcmCyvaWVfXwLnQTl//+siefoWsvzW43LaNU\n+3A18nCxVFSSbWosBN+0Zo4K9bSpEFGgUrJM5O98zv4+/SzCKFe2562usDzaRiEUW\n+iSJkzIUnSlcNc+XCY1rhG9HLXwKBgQDLpS6ATtMDSP9p+XYVMEN2CF8M3xvL\troOT\n+6wPYfp9fuagMgzNv9GB9SRyM/dM6mN+fkBqLp3EbDZT0UorHsg+YChoyBmctNqpW\n+j5/SrVyYe2xoRRgOzUbDstN44/LAhJLQnOXB7S2amo35zZ4FY6sw2w3QfkCildkB\n+mY3VhvESpwKBgErLtUPKfxJZN55UG7t/nS++U/wH6z3UE5YdDKizZLt5NinPyWjO\n+7yue8Ycb4zifSKA9zx/Zb2Zgr5l4DNmBp4eQdrQklsfbGHLBIp0LZTgE4DcaFyww\n+Cwv0OTpmrrlBb9NYWNAyYWqtv3kO3dlu5g8+Sd4cu8YyRZ+a/iSqNKKRAoGBAIPf\n+QICYCq8a60Lt5xiLe3QIsbx9EdvQ86Wqz3+3Z28uo3MO1xVNc9pNqO5oRAuzCUSj\n+pXz//g9duTKJ7RKp7M0w5Yu1d8TgnGeXdBCScN7RNf9DlvOm3IdH2wdy3TTr5MKw\n+h1wQQbLXGM9F5mlpBGeLwqNbznE6hh8yF5XJX30LAoGBAJqaa+yeZskti5ickNTF\n+vBBIXyYYBymdxfkf9vDSW1XcZEIVqo3+AGV+qHyTjURaty3QuEhSJEXem/obH5uE\n+y37+bnx8Se1IyJ/phYBLwOmtgZoBJALFhvjkFiGTF6naI8E/i4sbi5j/OEyShfWr\n+YFZuEKQJhiiMQznfNgthHU6H\n+-----END PRIVATE KEY-----\n+\"\"\", password=None)\n+\n+ATT_KEY = serialization.load_pem_private_key(b\"\"\"\n+-----BEGIN PRIVATE KEY-----\n+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDT449phHltY2aV\n+QIvaT4PUgNS7wDybnnjVO88NGB5PjfUaWY99oDQgOLJlejyVVqRO2wHxLaUMsbuc\n+oe0XbgSFJgrnGvG6yPbjSXeIfV5k2dJG60S4Fg2mZ1ieSabuPVKLA03frhbATmIf\n+Q+VTMlWLgLVxcT04iqph6VpjehnYke0VPMuN7OM6RsIOEhLcje0bvL4YjTYXH5j4\n+mPquc/ZEj/n6WJ6VsS27QygOBbaiGqHs54QnQi4gcgIgUmkR/bl2wL5s+729RBzS\n+v1FZfA5gdM9uEG3ogLHOC2uk+1Nuqcdk/tQxd30/2ulXubqDku/nNY2RSJrwD\tatt\n+qcyCliANAgMBAAECggEAK3Z7HVbKHZENIsJZrY8v6HAAsv5ssDMicALTpsjytrjU\n+tPH4B/nLl2xp03zuXmemTnKIBHOrbl4qsKdaXbr4fGNgSyVwvjKoydhxB3NH4IH5\n+qwhpUSVc6Ww7dkR/VFEJ1G/6Ek7AZfPuFqGzsYwalgHxtfJXb3iqGGloXA1Yrd5p\n+W2cTEhtSFZP/PQIEK773wYd3aYMw8OCqG2V5bw9N3xwY6KTC0Px8zyBlmAcUBPAj\n+QZL/DTGlMdD9+PJ3Ft3Zl2uS7ORn7xfXftvxv5IQdD+JBxV5zUIympKK/7KIVUfH\n+dfi93R7rqjL9EOP6bVQkg/WzYRLeVf/8km8HRGqtIQKBgQDxFxQ77t4EQqLPFofN\n+oRV7P3lvFqlJDTzAGBnjIT/ujT3SgoFUjRtfG27nWd1lycxv3tE0GTIw0LjJwvmg\n+VSFbQbPsmdp+f0jnNIiJayiG591j9Afmw06mnDodaQuSTp7K9idgpnFRGDQzwJHK\n+0DwSQzlzEXsPhGnXxpv+2Q+ANQKBgQDg/itVFBw3e5wC8boffi1AgnM97Qa/Y+5B\n+I2J9+cZD9iBkvE7kTwVUOI2Rr+XkQmSf+pT6L0yFXhQjIed004rpKVqzTvGL9VXJ\n+nBeADS4bxl1jsfkfvq9e6eNUK8vzyLoYQpS5/LK1oG5MPq3+30yzGIHM8JxxaOQ9\n+VdKQrUdLeQKBgAh35RAN3eKMbKeVhQOmCtkfa6aJRzz3qBCfSBmAS3yXnXpNdzl/\n+E10N26FouKwgoHu1eee4ktjAHB2KKbaGBvvrnORMqy4STn9AiyM4jl3euxoNslFa\n+vuJ/TlNGI0/qTw2WA+ATOJu+m+bNdtGG6vVBQz1VedsbrZQUt9oFydOZAoGAMlCk\n+4CHfLYk3GnF0bhaJiCOkIfUfzS1L2sVPAV0aOZiRJfX2rpf9WRhMkIgFoUY3uo8P\n+QePR+QFQ/4pVeIrWRc45ul+tJN94j92YY8qOxSdXOzRRwgeisFcdv3UL5zi8ZTB+\n+khkw3e1CvUpHHvhQ7rxMSsiEM9iBMjY/IJuflgECgYEAqiN3eg8cZjVrYEMcPLGx\n+wXknCG0KPc8EpDi1moNwS3z/TcUbfP8vnmT2lFHTAbvVBn+4fcLffkQBoGG3AaSH\n+3kc0HXLdy+rFcsXpX7hk9BM/Uey9dqBOAusLS6XxYhcAJ1xOI0kYWoeOhO8fcjNa\n+tf26cJGzfbbwf8kfisbv4Uk=\n+-----END PRIVATE KEY-----\n+\"\"\", password=None)\n+\n+\n+def _begin_cert() -> x509.CertificateBuilder:\n+\treturn x509.CertificateBuilder() \\\n+\t\t.serial_number(1) \\\n+\t\t.not_valid_before(datetime(2023, 1, 1)) \\\n+\t\t.not_valid_after(datetime(9999, 12, 31, 23, 59, 59))  # no well-defined expiration date, see RFC5280 4.1.2.5.\n+\n+\n+def generate_chain(ou_fields: List[str]) -> bytes:\n+\t# First, create the root CA\n+\troot_name = x509.Name([\n+\t\tx509.NameAttribute(NameOID.COMMON_NAME, \"qtestsign Root CA - NOT SECURE\"),\n+\t])\n+\t# only key_cert_sign=True\n+\troot_usage = x509.KeyUsage(False, False, False, False, False, True, False, False, False)\n+\troot_ski = x509.SubjectKeyIdentifier.from_public_key(ROOT_KEY.public_key())\n+\troot_cert_der = _begin_cert() \\\n+\t\t.subject_name(root_name) \\\n+\t\t.issuer_name(root_name) \\\n+\t\t.public_key(ROOT_KEY.public_key()) \\\n+\t\t.add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True) \\\n+\t\t.add_extension(root_usage, critical=True) \\\n+\t\t.add_extension(root_ski, critical=False) \\\n+\t\t.sign(ROOT_KEY, hashes.SHA256()) \\\n+\t\t.public_bytes(serialization.Encoding.DER)\n+\n+\t# Now, create the attestation certificate\n+\tatt_name = x509.Name([\n+\t\tx509.NameAttribute(NameOID.COMMON_NAME, \"qtestsign Attestation CA - NOT SECURE\"),\n+\t\t*[x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, ou) for ou in ou_fields],\n+\t])\n+\t# only digital_signature=True\n+\tatt_usage = x509.KeyUsage(True, False, False, False, False, False, False, False, False)\n+\tatt_cert_der = _begin_cert() \\\n+\t\t.subject_name(att_name) \\\n+\t\t.issuer_name(root_name) \\\n+\t\t.public_key(ATT_KEY.public_key()) \\\n+\t\t.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) \\\n+\t\t.add_extension(att_usage, critical=True) \\\n+\t\t.add_extension(x509.SubjectKeyIdentifier.from_public_key(ATT_KEY.public_key()), critical=False) \\\n+\t\t.add_extension(x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(root_ski), critical=False) \\\n+\t\t.sign(ROOT_KEY, hashes.SHA256()) \\\n+\t\t.public_bytes(serialization.Encoding.DER)\n+\n+\t# The certificate chain is the attestation and root certificate concatenated\n+\t# in DER format. Note: The order (first attestation, then root) is important!\n+\treturn att_cert_der + root_cert_der\ndiff --git a/tools/qcom/mkmbn/elf.py b/tools/qcom/mkmbn/elf.py\nnew file mode 100644\nindex 000000000000..a5c4dad5ee01\n--- /dev/null\n+++ b/tools/qcom/mkmbn/elf.py\n@@ -0,0 +1,205 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+# Copyright (C) 2021 Stephan Gerhold\n+# Data classes are based on the header definitions in the ELF(5) man page.\n+# Also see: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format\n+from __future__ import annotations\n+\n+import dataclasses\n+from dataclasses import dataclass\n+from struct import Struct\n+from typing import List, BinaryIO\n+\n+\n+@dataclass\n+class Ehdr:\n+\tei_magic: bytes\n+\tei_class: int\n+\tei_data: int\n+\tei_version: int\n+\tei_os_abi: int\n+\tei_abi_version: int\n+\te_type: int\n+\te_machine: int\n+\te_version: int\n+\t# Address size specific part\n+\te_entry: int = 0\n+\te_phoff: int = 0\n+\te_shoff: int = 0\n+\t# End part\n+\te_flags: int = 0\n+\te_ehsize: int = 0\n+\te_phentsize: int = 0\n+\te_phnum: int = 0\n+\te_shentsize: int = 0\n+\te_shnum: int = 0\n+\te_shstrndx: int = 0\n+\n+\tSTART_FORMAT = Struct('<4s5B7xHHL')\n+\tSTART_COUNT = 9\n+\tMEM_FORMAT32 = Struct('<LLL')\n+\tMEM_FORMAT64 = Struct('<QQQ')\n+\tMEM_COUNT = 3\n+\tEND_FORMAT = Struct('<L6H')\n+\tEND_COUNT = 7\n+\n+\tCLASS32 = 1\n+\tCLASS64 = 2\n+\n+\t@staticmethod\n+\tdef parse(b: bytes) -> Ehdr:\n+\t\thdr_unpack = Ehdr.START_FORMAT.unpack_from(b)\n+\t\thdr = Ehdr(*hdr_unpack)\n+\t\tassert hdr.ei_magic == b'\\x7fELF', f\"Invalid ELF header magic: {hdr.ei_magic}\"\n+\t\tassert hdr.ei_data == 1, \"Only little endian supported at the moment\"\n+\t\tassert hdr.ei_version == 1, f\"Unexpected ei_version: {hdr.ei_version}\"\n+\t\tassert hdr.e_version == 1, f\"Unexpected e_version: {hdr.e_version}\"\n+\n+\t\tif hdr.ei_class == Ehdr.CLASS32:\n+\t\t\tmem_format = Ehdr.MEM_FORMAT32\n+\t\telse:\n+\t\t\tassert hdr.ei_class == Ehdr.CLASS64, f\"Unexpected ei_class: {hdr.ei_class}\"\n+\t\t\tmem_format = Ehdr.MEM_FORMAT64\n+\n+\t\tmem_unpack = mem_format.unpack_from(b, Ehdr.START_FORMAT.size)\n+\t\tend_unpack = Ehdr.END_FORMAT.unpack_from(b, Ehdr.START_FORMAT.size + mem_format.size)\n+\t\treturn Ehdr(*hdr_unpack, *mem_unpack, *end_unpack)\n+\n+\tdef save(self, f: BinaryIO) -> int:\n+\t\tunpack = dataclasses.astuple(self)\n+\t\twritten = f.write(Ehdr.START_FORMAT.pack(*unpack[:Ehdr.START_COUNT]))\n+\n+\t\tif self.ei_class == Ehdr.CLASS32:\n+\t\t\tmem_format = Ehdr.MEM_FORMAT32\n+\t\telse:\n+\t\t\tmem_format = Ehdr.MEM_FORMAT64\n+\t\twritten += f.write(\n+\t\t\tmem_format.pack(*unpack[Ehdr.START_COUNT:Ehdr.START_COUNT + Ehdr.MEM_COUNT]))\n+\t\twritten += f.write(Ehdr.END_FORMAT.pack(*unpack[-Ehdr.END_COUNT:]))\n+\t\treturn written\n+\n+\n+@dataclass\n+class Phdr:\n+\tp_type: int\n+\tp_offset: int\n+\tp_vaddr: int\n+\tp_paddr: int\n+\tp_filesz: int\n+\tp_memsz: int\n+\tp_flags: int\n+\tp_align: int\n+\n+\tdata = None\n+\n+\tFORMAT32 = Struct('<8L')\n+\tFORMAT64 = Struct('<LL6Q')\n+\n+\tPT_NULL = 0\n+\tPT_LOAD = 1\n+\n+\t@staticmethod\n+\tdef parse(b: bytes, offset: int, ei_class: int) -> Phdr:\n+\t\tif ei_class == Ehdr.CLASS32:\n+\t\t\tunpack = list(Phdr.FORMAT32.unpack_from(b, offset))\n+\t\telse:\n+\t\t\tunpack = list(Phdr.FORMAT64.unpack_from(b, offset))\n+\n+\t\t\t# ELFCLASS64 has flags directly before offset for alignment\n+\t\t\tflags = unpack.pop(1)\n+\t\t\tunpack.insert(-1, flags)\n+\n+\t\treturn Phdr(*unpack)\n+\n+\tdef save(self, f: BinaryIO, ei_class: int) -> int:\n+\t\tunpack = dataclasses.astuple(self)\n+\n+\t\tif ei_class == Ehdr.CLASS32:\n+\t\t\treturn f.write(Phdr.FORMAT32.pack(*unpack))\n+\t\telse:\n+\t\t\tunpack = list(unpack)\n+\n+\t\t\t# ELFCLASS64 has flags directly before offset for alignment\n+\t\t\tflags = unpack.pop(-2)\n+\t\t\tunpack.insert(1, flags)\n+\n+\t\t\treturn f.write(Phdr.FORMAT64.pack(*unpack))\n+\n+\n+def _pad(f: BinaryIO, offset: int, pos: int) -> int:\n+\tassert offset >= pos, f\"{offset} >= {pos}\"\n+\tpad = offset - pos\n+\tif pad:\n+\t\tassert f.write(b'\\0' * pad) == pad\n+\treturn offset\n+\n+\n+def align(i: int, alignment: int) -> int:\n+\tmask = max(alignment - 1, 0)\n+\treturn (i + mask) & ~mask\n+\n+\n+@dataclass\n+class Elf:\n+\tehdr: Ehdr\n+\tphdrs: List[Phdr]\n+\n+\tdef total_header_size(self):\n+\t\treturn self.ehdr.e_phoff + len(self.phdrs) * self.ehdr.e_phentsize\n+\n+\t@staticmethod\n+\tdef parse(b: bytes) -> Elf:\n+\t\tehdr = Ehdr.parse(b)\n+\t\tview = memoryview(b)\n+\n+\t\t# Parse program headers\n+\t\tphdrs = []\n+\t\toffset = ehdr.e_phoff\n+\t\tfor i in range(ehdr.e_phnum):\n+\t\t\tphdr = Phdr.parse(b, offset, ehdr.ei_class)\n+\t\t\tphdrs.append(phdr)\n+\n+\t\t\t# Store data if necessary\n+\t\t\tif phdr.p_filesz and phdr.p_offset:\n+\t\t\t\tphdr.data = view[phdr.p_offset:phdr.p_offset + phdr.p_filesz]\n+\n+\t\t\toffset += ehdr.e_phentsize\n+\n+\t\treturn Elf(ehdr, phdrs)\n+\n+\tdef update(self):\n+\t\t# Rearrange all segments according to their alignment\n+\t\tpos = self.total_header_size()\n+\t\tfor phdr in sorted(self.phdrs, key=lambda phdr: phdr.p_offset):\n+\t\t\tif phdr.p_offset and phdr.p_filesz:\n+\t\t\t\tphdr.p_offset = align(pos, phdr.p_align)\n+\t\t\t\tpos = phdr.p_offset + phdr.p_filesz\n+\n+\t\t# Ensure program header count is correct\n+\t\tself.ehdr.e_phnum = len(self.phdrs)\n+\n+\t\t# TODO: Clear out sections for now. Those are not read at the moment.\n+\t\t# Also, I don't think the Qualcomm firmware loader has any use for these.\n+\t\tself.ehdr.e_shoff = 0\n+\t\tself.ehdr.e_shnum = 0\n+\t\tself.ehdr.e_shstrndx = 0\n+\n+\tdef save_header(self, f: BinaryIO) -> int:\n+\t\tpos = self.ehdr.save(f)\n+\t\tpos = _pad(f, self.ehdr.e_phoff, pos)\n+\n+\t\t# Write program headers\n+\t\tfor phdr in self.phdrs:\n+\t\t\tpos += phdr.save(f, self.ehdr.ei_class)\n+\n+\t\treturn pos\n+\n+\tdef save(self, f: BinaryIO) -> int:\n+\t\tpos = self.save_header(f)\n+\n+\t\t# Write segment data\n+\t\tfor phdr in sorted(self.phdrs, key=lambda phdr: phdr.p_offset):\n+\t\t\tif phdr.data:\n+\t\t\t\tpos = _pad(f, phdr.p_offset, pos)\n+\t\t\t\tpos += f.write(phdr.data)\n+\n+\t\treturn pos\ndiff --git a/tools/qcom/mkmbn/hashseg.py b/tools/qcom/mkmbn/hashseg.py\nnew file mode 100644\nindex 000000000000..fe74761ae8df\n--- /dev/null\n+++ b/tools/qcom/mkmbn/hashseg.py\n@@ -0,0 +1,356 @@\n+# SPDX-License-Identifier: GPL-2.0-only AND BSD-3-Clause\n+# Copyright (C) 2021-2023 Stephan Gerhold (GPL-2.0-only)\n+# MBN header format adapted from:\n+#   - signlk: https://git.linaro.org/landing-teams/working/qualcomm/signlk.git\n+#   - coreboot (util/qualcomm/mbn_tools.py, util/cbfstool/platform_fixups.c)\n+# Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. (BSD-3-Clause)\n+# See also:\n+#   - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf\n+#   - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v2-0.pdf\n+from __future__ import annotations\n+\n+import dataclasses\n+import hashlib\n+from dataclasses import dataclass\n+from io import BytesIO\n+from struct import Struct\n+\n+from . import cert\n+from . import elf\n+\n+# A typical Qualcomm firmware might have the following program headers:\n+#     LOAD off    0x00000800 vaddr 0x86400000 paddr 0x86400000 align 2**11\n+#          filesz 0x00001000 memsz 0x00001000 flags rwx\n+#\n+# The signed version will then look like:\n+#     NULL off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**0\n+#          filesz 0x000000e8 memsz 0x00000000 flags --- 7000000\n+#     NULL off    0x00001000 vaddr 0x86401000 paddr 0x86401000 align 2**12\n+#          filesz 0x00000988 memsz 0x00001000 flags --- 2200000\n+#     LOAD off    0x00002000 vaddr 0x86400000 paddr 0x86400000 align 2**11\n+#          filesz 0x00001000 memsz 0x00001000 flags rwx\n+#\n+# The second NULL program header with off 0x1000 and filesz 0x988 is the actual\n+# \"hash table segment\" or shortly \"hash segment\" (see Figure 2 on page 6 in the PDF).\n+# It contains the MBN header specified below, then a couple of hashes (e.g. SHA256):\n+#   1. Hash of ELF header and program headers\n+#   2. Empty hash for hash segment\n+#   3. Hashes for data of each memory segment (described by program header)\n+# Finally, it contains an RSA signature and the concatenated certificate chain.\n+#\n+# The first NULL program header is never loaded anywhere, because\n+# vaddr = paddr = memsz = 0. However, the \"off\" and \"filesz\" cover exactly\n+# the ELF header (including all program headers). It is a placeholder so that\n+# each hash covers the data of exactly one program header.\n+\n+# For definitions of the ELF PHDR flags used by Qualcomm, see:\n+# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L108-L189\n+PHDR_FLAGS_SEGMENT_TYPE_MASK\t= 0x07000000\n+PHDR_FLAGS_SEGMENT_TYPE_SHIFT\t= 0x18\n+PHDR_FLAGS_SEGMENT_TYPE_HASH\t= (0x2 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT)\n+PHDR_FLAGS_SEGMENT_TYPE_HDR\t= (0x7 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT)\n+\n+PDHR_FLAGS_ACCESS_TYPE_MASK\t= 0x00E00000\n+PHDR_FLAGS_ACCESS_TYPE_SHIFT\t= 0x15\n+PHDR_FLAGS_ACCESS_TYPE_RO\t= (0x1 << PHDR_FLAGS_ACCESS_TYPE_SHIFT)\n+\n+# Flags we use for placeholder for hash over ELF header and hash segment\n+PHDR_FLAGS_HDR_PLACEHOLDER = PHDR_FLAGS_SEGMENT_TYPE_HDR\n+PHDR_FLAGS_HASH_SEGMENT = (PHDR_FLAGS_SEGMENT_TYPE_HASH | PHDR_FLAGS_ACCESS_TYPE_RO)\n+\n+EXTRA_PHDRS = 2  # header placeholder + hash segment\n+\n+# Note: None of the alignments seem to be truly required,\n+# this could probably be reduced to get smaller file sizes.\n+HASH_SEG_ALIGN = 0x1000\n+CERT_CHAIN_ALIGN = 16\n+\n+# According to the v2.0 PDF the metadata is 128 bytes long, but this does not\n+# seem to work. All official firmware seems to use 120 bytes instead.\n+MBN_V6_METADATA_SIZE = 120\n+\n+# See OEM Metadata 2.0 definition in coreboot source code:\n+# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691\n+MBN_V7_OEM_2_0_METADATA_SIZE = 224\n+\n+\n+@dataclass\n+class _HashSegment:\n+\timage_id: int = 0  # Type of image (unused?)\n+\tversion: int = 0  # Header version number\n+\n+\thash_size = 0\n+\tsignature_size = 0\n+\tcert_chain_size = 0\n+\ttotal_size = 0\n+\n+\thashes = []\n+\tsignature = b''\n+\tcert_chain = b''\n+\n+\tFORMAT = Struct('<10L')\n+\tHash = hashlib.sha256\n+\n+\t@property\n+\tdef size_with_header(self):\n+\t\treturn self.FORMAT.size + self.total_size\n+\n+\tdef update(self, dest_addr: int):\n+\t\tself.hash_size = len(self.hashes) * self.Hash().digest_size\n+\t\tself.signature_size = len(self.signature)\n+\t\tself.cert_chain_size = len(self.cert_chain)\n+\t\tself.total_size = self.hash_size + self.signature_size + self.cert_chain_size\n+\n+\tdef check(self):\n+\t\tassert len(self.hashes) * self.Hash().digest_size == self.hash_size\n+\t\tassert len(self.signature) == self.signature_size\n+\t\tassert len(self.cert_chain) == self.cert_chain_size\n+\n+\tdef pack_header(self):\n+\t\tself.check()\n+\t\treturn self.FORMAT.pack(*dataclasses.astuple(self))\n+\n+\tdef pack(self):\n+\t\treturn self.pack_header() \\\n+\t\t\t+ b''.join(self.hashes) \\\n+\t\t\t+ self.signature + self.cert_chain\n+\n+\n+@dataclass\n+class HashSegmentV3(_HashSegment):\n+\tversion: int = 3  # Header version number\n+\n+\tflash_addr: int = 0  # Location of image in flash (historical)\n+\tdest_addr: int = 0  # Physical address of loaded hash segment data\n+\ttotal_size: int = 0  # = hash_size + signature_size + cert_chain_size\n+\thash_size: int = 0  # Size of hashes for all program segments\n+\tsignature_addr: int = 0  # Physical address of loaded attestation signature\n+\tsignature_size: int = 0  # Size of attestation signature\n+\tcert_chain_addr: int = 0  # Physical address of loaded certificate chain\n+\tcert_chain_size: int = 0  # Size of certificate chain\n+\n+\tdef update(self, dest_addr: int):\n+\t\tsuper().update(dest_addr)\n+\t\tself.dest_addr = dest_addr + self.FORMAT.size\n+\t\tself.signature_addr = self.dest_addr + self.hash_size\n+\t\tself.cert_chain_addr = self.signature_addr + self.signature_size\n+\n+\n+@dataclass\n+class HashSegmentV5(_HashSegment):\n+\tversion: int = 5  # Header version number\n+\n+\tsignature_size_qcom: int = 0  # Size of signature from Qualcomm\n+\tcert_chain_size_qcom: int = 0  # Size of certificate chain from Qualcomm\n+\ttotal_size: int = 0  # = hash_size + signature_size + cert_chain_size\n+\thash_size: int = 0  # Size of hashes for all program segments\n+\tsignature_addr: int = 0xffffffff  # unused?\n+\tsignature_size: int = 0  # Size of attestation signature\n+\tcert_chain_addr: int = 0xffffffff  # unused?\n+\tcert_chain_size: int = 0  # Size of certificate chain\n+\n+\tsignature_qcom = b''\n+\tcert_chain_qcom = b''\n+\n+\tdef update(self, dest_addr: int):\n+\t\tsuper().update(dest_addr)\n+\t\tself.signature_size_qcom = len(self.signature_qcom)\n+\t\tself.cert_chain_size_qcom = len(self.cert_chain_qcom)\n+\t\tself.total_size += self.signature_size_qcom + self.cert_chain_size_qcom\n+\n+\tdef check(self):\n+\t\tsuper().check()\n+\t\tassert len(self.signature_qcom) == self.signature_size_qcom\n+\t\tassert len(self.cert_chain_qcom) == self.cert_chain_size_qcom\n+\n+\tdef pack(self):\n+\t\treturn self.pack_header() \\\n+\t\t\t+ b''.join(self.hashes) \\\n+\t\t\t+ self.signature_qcom + self.cert_chain_qcom \\\n+\t\t\t+ self.signature + self.cert_chain\n+\n+\n+@dataclass\n+class HashSegmentV6(HashSegmentV5):\n+\tversion: int = 6  # Header version number\n+\n+\tmetadata_size_qcom: int = 0  # Size of metadata from Qualcomm\n+\tmetadata_size: int = 0  # Size of metadata\n+\n+\tmetadata_qcom = b''\n+\tmetadata = b''\n+\n+\tFORMAT = Struct('<12L')\n+\tHash = hashlib.sha384\n+\n+\tdef update(self, dest_addr: int):\n+\t\tsuper().update(dest_addr)\n+\t\tself.metadata_size_qcom = len(self.metadata_qcom)\n+\t\tself.metadata_size = len(self.metadata)\n+\t\tself.total_size += self.metadata_size_qcom + self.metadata_size\n+\n+\tdef check(self):\n+\t\tsuper().check()\n+\t\tassert len(self.metadata_qcom) == self.metadata_size_qcom\n+\t\tassert len(self.metadata) == self.metadata_size\n+\n+\tdef pack(self):\n+\t\treturn self.pack_header() \\\n+\t\t\t+ self.metadata_qcom + self.metadata \\\n+\t\t\t+ b''.join(self.hashes) \\\n+\t\t\t+ self.signature_qcom + self.cert_chain_qcom \\\n+\t\t\t+ self.signature + self.cert_chain\n+\n+\n+@dataclass\n+# Information from MBNv7 definition in Coreboot source code:\n+# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691\n+class HashSegmentV7(_HashSegment):\n+\tversion: int = 7  # Header version number\n+\n+\tcommon_metadata_size: int = 24  # Size of \"common metadata\" below\n+\tmetadata_size_qcom: int = 0  # Size of metadata from Qualcomm\n+\tmetadata_size: int = 0  # Size of metadata from OEM\n+\thash_size: int = 0  # Size of hashes for all program segments\n+\tsignature_size_qcom: int = 0  # Size of signature from Qualcomm\n+\tcert_chain_size_qcom: int = 0  # Size of certificate chain from Qualcomm\n+\tsignature_size: int = 0  # Size of attestation signature\n+\tcert_chain_size: int = 0  # Size of certificate chain\n+\n+\t# Common metadata, placed directly after MBNv7 header\n+\tcommon_metadata_major_version: int = 0\n+\tcommon_metadata_minor_version: int = 0\n+\tsoftware_id: int = 0  # Type of software image, mandatory\n+\tsecondary_software_id: int = 0\n+\thash_table_algorithm: int = 3  # SHA384\n+\tmeasurement_register_target: int = 0\n+\n+\tmetadata_qcom = b''\n+\tmetadata = b''\n+\tsignature_qcom = b''\n+\tcert_chain_qcom = b''\n+\n+\tFORMAT = Struct('<16L')\n+\tHash = hashlib.sha384\n+\n+\tdef update(self, dest_addr: int):\n+\t\tsuper().update(dest_addr)\n+\t\tself.metadata_size_qcom = len(self.metadata_qcom)\n+\t\tself.metadata_size = len(self.metadata)\n+\t\tself.signature_size_qcom = len(self.signature_qcom)\n+\t\tself.cert_chain_size_qcom = len(self.cert_chain_qcom)\n+\t\t# self.common_metadata_size is already included as part of the header\n+\t\tself.total_size += self.metadata_size_qcom + self.metadata_size\n+\t\tself.total_size += self.signature_size_qcom + self.cert_chain_size_qcom\n+\n+\tdef check(self):\n+\t\tsuper().check()\n+\t\tassert len(self.metadata_qcom) == self.metadata_size_qcom\n+\t\tassert len(self.metadata) == self.metadata_size\n+\t\tassert len(self.signature_qcom) == self.signature_size_qcom\n+\t\tassert len(self.cert_chain_qcom) == self.cert_chain_size_qcom\n+\n+\tdef pack(self):\n+\t\treturn self.pack_header() \\\n+\t\t\t+ self.metadata_qcom + self.metadata \\\n+\t\t\t+ b''.join(self.hashes) \\\n+\t\t\t+ self.signature_qcom + self.cert_chain_qcom \\\n+\t\t\t+ self.signature + self.cert_chain\n+\n+HashSegment = {\n+\t3: HashSegmentV3,\n+\t5: HashSegmentV5,\n+\t6: HashSegmentV6,\n+\t7: HashSegmentV7,\n+}\n+\n+\n+def drop(elff: elf.Elf):\n+\t# Drop existing hash segments\n+\telff.phdrs = [phdr for phdr in elff.phdrs if phdr.p_type != elf.Phdr.PT_NULL\n+\t\t      or (phdr.p_flags & PHDR_FLAGS_SEGMENT_TYPE_MASK) not in\n+\t\t      [PHDR_FLAGS_SEGMENT_TYPE_HASH, PHDR_FLAGS_SEGMENT_TYPE_HDR]]\n+\n+\n+def generate(elff: elf.Elf, version: int, sw_id: int):\n+\tdrop(elff)\n+\tassert elff.phdrs, \"Need at least one program header\"\n+\n+\thash_seg = HashSegment[version]()\n+\n+\tif version == 6:\n+\t\t# TODO: Figure out metadata format and fill this with useful data\n+\t\thash_seg.metadata = b'\\0' * MBN_V6_METADATA_SIZE\n+\n+\t# Software ID is mandatory for MBN v7\n+\tif version == 7:\n+\t\thash_seg.software_id = sw_id\n+\t\t# The format is documented in Coreboot util/qualcomm/mbn_tools.py\n+\t\t# (see class Boot_Hdr), but for simplicity we just keep this empty.\n+\t\thash_seg.metadata = b'\\0' * MBN_V7_OEM_2_0_METADATA_SIZE\n+\n+\t# Generate hash for all existing segments with data\n+\tdigest_size = hash_seg.Hash().digest_size\n+\thash_seg.hashes = [b'\\0' * digest_size] * (len(elff.phdrs) + EXTRA_PHDRS)\n+\tfor i, phdr in enumerate(elff.phdrs, start=EXTRA_PHDRS):\n+\t\tif phdr.data:\n+\t\t\thash_seg.hashes[i] = hash_seg.Hash(phdr.data).digest()\n+\ttotal_hashes_size = len(hash_seg.hashes) * digest_size\n+\n+\t# Generate certificate chain with specified OU fields (for < v6)\n+\t# on >= v6 this is part of the metadata instead\n+\tou_fields = []\n+\tif version < 6:\n+\t\tou_fields = [\n+\t\t\t# Note: The SW_ID is checked by the firmware on some platforms (even if secure boot\n+\t\t\t# is disabled), so it must match the firmware type being signed. Everything else seems\n+\t\t\t# to be mostly ignored when secure boot is off and is just added here to match the\n+\t\t\t# documentation and better mimic the official firmware.\n+\t\t\t\"01 %016X SW_ID\" % sw_id,\n+\t\t\t\"02 %016X HW_ID\" % 0,\n+\t\t\t\"03 %016X DEBUG\" % 2,  # DISABLED\n+\t\t\t\"04 %04X OEM_ID\" % 0,\n+\t\t\t\"05 %08X SW_SIZE\" % (hash_seg.FORMAT.size + total_hashes_size),\n+\t\t\t\"06 %04X MODEL_ID\" % 0,\n+\t\t\t\"07 %04X SHA256\" % 1,\n+\t\t]\n+\thash_seg.cert_chain = cert.generate_chain(ou_fields)\n+\thash_seg.cert_chain = hash_seg.cert_chain.ljust(elf.align(len(hash_seg.cert_chain), CERT_CHAIN_ALIGN), b'\\xff')\n+\t# hash_seg.cert_chain = b''  # uncomment this to omit the certificate chain in the signed image\n+\n+\t# TODO: Generate actual signature with our generated attestation certificate!\n+\t# There are different signature schemes that could be implemented (RSASSA-PKCS#1 v1.5\n+\t# RSASSA-PSS, ECDSA over P-384) but it's not entirely clear yet which chipsets supports/\n+\t# uses which. The signature does not seem to be checked on devices without secure boot,\n+\t# so just use a dummy value for now.\n+\thash_seg.signature = b'\\xff' * (cert.ATT_KEY.key_size // 8)\n+\t# hash_seg.signature = b''  # uncomment this to omit the signature in the signed image\n+\n+\t# Align maximum end address to get address for hash table header, then update header\n+\thash_addr = elf.align(max(phdr.p_paddr + phdr.p_memsz for phdr in elff.phdrs), HASH_SEG_ALIGN)\n+\thash_seg.update(hash_addr)\n+\n+\t# Insert new hash NULL segment\n+\thash_phdr = elf.Phdr(elf.Phdr.PT_NULL, HASH_SEG_ALIGN, hash_addr, hash_addr, hash_seg.size_with_header,\n+\t\t\t\t\t\t elf.align(hash_seg.size_with_header, HASH_SEG_ALIGN),\n+\t\t\t\t\t\t PHDR_FLAGS_HASH_SEGMENT, HASH_SEG_ALIGN)\n+\telff.phdrs.insert(0, hash_phdr)\n+\n+\t# Insert new ELF header placeholder program header\n+\thdr_hash_phdr = elf.Phdr(elf.Phdr.PT_NULL, 0, 0, 0, 0, 0, PHDR_FLAGS_HDR_PLACEHOLDER, 0)\n+\telff.phdrs.insert(0, hdr_hash_phdr)\n+\n+\t# Now determine size of ELF header (including program headers)\n+\thdr_hash_phdr.p_filesz = elff.total_header_size()\n+\n+\t# Recompute attributes to match final output (e.g. adjust e_phnum)\n+\telff.update()\n+\n+\t# Compute the hash for the ELF header\n+\twith BytesIO() as hdr_io:\n+\t\telff.save_header(hdr_io)\n+\t\thash_seg.hashes[0] = hash_seg.Hash(hdr_io.getbuffer()).digest()\n+\n+\t# And finally, assemble the hash segment\n+\thash_phdr.data = hash_seg.pack()\n+\tassert len(hash_phdr.data) == hash_phdr.p_filesz\n\\ No newline at end of file\n","prefixes":["v6","2/6"]}