get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2196901/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2196901,
    "url": "http://patchwork.ozlabs.org/api/patches/2196901/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/20260216-feature_fwumdata-v3-5-9ecc5b10456d@bootlin.com/",
    "project": {
        "id": 18,
        "url": "http://patchwork.ozlabs.org/api/projects/18/?format=api",
        "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": "<20260216-feature_fwumdata-v3-5-9ecc5b10456d@bootlin.com>",
    "list_archive_url": null,
    "date": "2026-02-16T13:35:35",
    "name": "[v3,5/6] tools: Add support for fwumdata tool",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "18534f53793baccc4e090156f484ce87a9aaf2f2",
    "submitter": {
        "id": 80520,
        "url": "http://patchwork.ozlabs.org/api/people/80520/?format=api",
        "name": "Kory Maincent",
        "email": "kory.maincent@bootlin.com"
    },
    "delegate": {
        "id": 96103,
        "url": "http://patchwork.ozlabs.org/api/users/96103/?format=api",
        "username": "apalos",
        "first_name": "Ilias",
        "last_name": "Apalodimas",
        "email": "apalos@gmail.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/20260216-feature_fwumdata-v3-5-9ecc5b10456d@bootlin.com/mbox/",
    "series": [
        {
            "id": 492310,
            "url": "http://patchwork.ozlabs.org/api/series/492310/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=492310",
            "date": "2026-02-16T13:35:30",
            "name": "Add support for fwumdata",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/492310/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2196901/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2196901/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=bootlin.com header.i=@bootlin.com header.a=rsa-sha256\n header.s=dkim header.b=aGEcM7+I;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)",
            "phobos.denx.de;\n dmarc=pass (p=reject dis=none) header.from=bootlin.com",
            "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=bootlin.com header.i=@bootlin.com header.b=\"aGEcM7+I\";\n\tdkim-atps=neutral",
            "phobos.denx.de;\n dmarc=pass (p=reject dis=none) header.from=bootlin.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=kory.maincent@bootlin.com"
        ],
        "Received": [
            "from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\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 4fF3hq5Q1tz1xpY\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 17 Feb 2026 00:36:43 +1100 (AEDT)",
            "from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id ED95483DE4;\n\tMon, 16 Feb 2026 14:36:14 +0100 (CET)",
            "by phobos.denx.de (Postfix, from userid 109)\n id 8677F83B8A; Mon, 16 Feb 2026 14:36:11 +0100 (CET)",
            "from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id 6CC9083C6C\n for <u-boot@lists.denx.de>; Mon, 16 Feb 2026 14:36:07 +0100 (CET)",
            "from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233])\n by smtpout-03.galae.net (Postfix) with ESMTPS id 19CF44E40E6C;\n Mon, 16 Feb 2026 13:36:07 +0000 (UTC)",
            "from mail.galae.net (mail.galae.net [212.83.136.155])\n by smtpout-01.galae.net (Postfix) with ESMTPS id E1EDA606CF;\n Mon, 16 Feb 2026 13:36:06 +0000 (UTC)",
            "from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon)\n with ESMTPSA id D18DC10369152; Mon, 16 Feb 2026 14:36:02 +0100 (CET)"
        ],
        "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 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED,\n SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim;\n t=1771248965; h=from:subject:date:message-id:to:cc:mime-version:content-type:\n content-transfer-encoding:in-reply-to:references;\n bh=51me91ixnxQVem55J1N/6ir4dGRFtM9d3Fzm1cKrvYk=;\n b=aGEcM7+I9HxyHEKvUZ6bnAEd+9dOWDkzOZQB0mdijvFdQa34/dW+e6lIXkc83K0GCSi6aN\n fdTaOWKES4h14z/x7F9n4DHe5ioOfEq/n8I8EkzqlCHuFs+nIw+r9aIvFUgypBmxvSJ4AD\n QakDwifadBGabL6+utYxft2OUOqur6w4dU5+zfpvec/O5bsdZY34IcAClgRF50CvuBXOtw\n ZlA/VvhRf45IHwTKDoaRExwnQI09ZD1h5V9a0SBF68EN06xvbW9dfzOUq+x5nmOu7u1Bu3\n 1+AGsKIsJEl9Z6srgdPm0IIf+DWVIitqBglvrJyUt+S4hL794/2SJrzcYuhAag==",
        "From": "Kory Maincent <kory.maincent@bootlin.com>",
        "Date": "Mon, 16 Feb 2026 14:35:35 +0100",
        "Subject": "[PATCH v3 5/6] tools: Add support for fwumdata tool",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "7bit",
        "Message-Id": "<20260216-feature_fwumdata-v3-5-9ecc5b10456d@bootlin.com>",
        "References": "<20260216-feature_fwumdata-v3-0-9ecc5b10456d@bootlin.com>",
        "In-Reply-To": "<20260216-feature_fwumdata-v3-0-9ecc5b10456d@bootlin.com>",
        "To": "u-boot@lists.denx.de",
        "Cc": "Thomas Petazzoni <thomas.petazzoni@bootlin.com>,\n  Tom Rini <trini@konsulko.com>,\n  Patrice Chotard <patrice.chotard@foss.st.com>,\n  Paul HENRYS <paul.henrys_ext@softathome.com>,\n  Sughosh Ganu <sughosh.ganu@arm.com>, Greg Malysa <malysagreg@gmail.com>,\n  Arturs Artamonovs <arturs.artamonovs@analog.com>,\n  Vasileios Bimpikas <vasileios.bimpikas@analog.com>,\n  Utsav Agarwal <utsav.agarwal@analog.com>,\n  Nathan Barrett-Morrison <nathan.morrison@timesys.com>,\n  Peng Fan <peng.fan@nxp.com>, Simon Glass <sjg@chromium.org>, =?utf-8?q?Duj?=\n\t=?utf-8?q?e_Mihanovi=C4=87?= <duje@dujemihanovic.xyz>,\n  Stefan Roese <stefan.roese@mailbox.org>,\n  Mattijs Korpershoek <mkorpershoek@kernel.org>,\n  Sumit Garg <sumit.garg@kernel.org>, Heiko Schocher <hs@nabladev.com>,\n  Alif Zakuan Yuslaimi <alif.zakuan.yuslaimi@altera.com>,\n  E Shattow <e@freeshell.de>, Raymond Mao <raymondmaoca@gmail.com>,\n  Jan Kiszka <jan.kiszka@siemens.com>, Shiji Yang <yangshiji66@outlook.com>,\n  Daniel Golle <daniel@makrotopia.org>,\n  Heinrich Schuchardt <xypron.glpk@gmx.de>,\n  Ilias Apalodimas <ilias.apalodimas@linaro.org>,\n  Leonard Anderweit <l.anderweit@phytec.de>,\n  Kory Maincent <kory.maincent@bootlin.com>, Yao Zi <me@ziyao.cc>",
        "X-Mailer": "b4 0.14-dev-d4707",
        "X-Last-TLS-Session-Version": "TLSv1.3",
        "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": "Add a new fwumdata tool to allows users to read, display, and modify FWU\n(Firmware Update) metadata from Linux userspace. It provides functionality\nsimilar to fw_printenv/fw_setenv but for FWU metadata. Users can view\nmetadata, change active/previous bank indices, modify bank states, and set\nimage acceptance flags. Configuration is done via fwumdata.config file.\n\nSigned-off-by: Kory Maincent <kory.maincent@bootlin.com>\n---\n MAINTAINERS                        |   4 +\n doc/develop/uefi/fwu_updates.rst   |   4 +-\n doc/fwumdata.1                     | 222 ++++++++++\n tools/.gitignore                   |   1 +\n tools/fwumdata_src/Kconfig         |  11 +\n tools/fwumdata_src/fwumdata.c      | 854 +++++++++++++++++++++++++++++++++++++\n tools/fwumdata_src/fwumdata.config |  33 ++\n tools/fwumdata_src/fwumdata.h      | 138 ++++++\n tools/fwumdata_src/fwumdata.mk     |   5 +-\n 9 files changed, 1270 insertions(+), 2 deletions(-)",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 9d954be4a9d..d680b193033 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -1242,11 +1242,15 @@ F:\tdrivers/watchdog/sbsa_gwdt.c\n \n FWU Multi Bank Update\n M:\tSughosh Ganu <sughosh.ganu@arm.com>\n+M:\tKory Maincent <kory.maincent@bootlin.com>\n S:\tMaintained\n T:\tgit https://source.denx.de/u-boot/custodians/u-boot-efi.git\n+F:\tdoc/fwumdata.1\n F:\tdoc/mkfwumdata.1\n F:\tlib/fwu_updates/*\n F:\tdrivers/fwu-mdata/*\n+F:\ttools/fwumdata_src/fwumdata.c\n+F:\ttools/fwumdata_src/fwumdata.h\n F:\ttools/fwumdata_src/mkfwumdata.c\n \n GATEWORKS_SC\ndiff --git a/doc/develop/uefi/fwu_updates.rst b/doc/develop/uefi/fwu_updates.rst\nindex 84713581459..c592106f8a8 100644\n--- a/doc/develop/uefi/fwu_updates.rst\n+++ b/doc/develop/uefi/fwu_updates.rst\n@@ -66,7 +66,9 @@ FWU Metadata\n U-Boot supports both versions(1 and 2) of the FWU metadata defined in\n the two revisions of the specification. Support can be enabled for\n either of the two versions through a config flag. The mkfwumdata tool\n-can generate metadata for both the supported versions.\n+can generate metadata for both the supported versions. On the target side,\n+the fwumdata tool can read and update FWU metadata located in memory,\n+similarly to how fw_printenv/fw_setenv works.\n \n Setting up the device for GPT partitioned storage\n -------------------------------------------------\ndiff --git a/doc/fwumdata.1 b/doc/fwumdata.1\nnew file mode 100644\nindex 00000000000..66a53fc9403\n--- /dev/null\n+++ b/doc/fwumdata.1\n@@ -0,0 +1,222 @@\n+.\\\" SPDX-License-Identifier: GPL-2.0-or-later\n+.\\\" Copyright (C) 2025 Kory Maincent <kory.maincent@bootlin.com>\n+.TH FWUMDATA 1 2025 U-Boot\n+.SH NAME\n+fwumdata \\- read, display, and modify FWU metadata\n+.\n+.SH SYNOPSIS\n+.SY fwumdata\n+.OP \\-c config\n+.OP \\-l\n+.OP \\-u\n+.OP \\-a bankid\n+.OP \\-p bankid\n+.RB [ \\-s\n+.IR bankid \" \" state ]\n+.OP \\-i imageid\n+.OP \\-b bankid\n+.OP \\-A\n+.OP \\-C\n+.OP \\-B num_banks\n+.OP \\-I num_images\n+.YS\n+.SY fwumdata\n+.B \\-h\n+.YS\n+.\n+.SH DESCRIPTION\n+.B fwumdata\n+reads, displays, and modifies FWU (Firmware Update) metadata from Linux\n+userspace.\n+.PP\n+The tool operates on FWU metadata stored on block or MTD devices, allowing\n+userspace manipulation of firmware update state including active bank\n+selection, image acceptance, and bank state management.\n+.\n+.SH OPTIONS\n+.TP\n+.BR \\-c \", \" \\-\\-config \" \\fIfile\\fR\"\n+Use custom configuration file. By default, the tool searches for\n+.I ./fwumdata.config\n+then\n+.IR /etc/fwumdata.config .\n+.\n+.TP\n+.BR \\-l \", \" \\-\\-list\n+Display detailed metadata information including all GUIDs, image entries,\n+and bank information. Without this option, only a summary is shown.\n+.\n+.TP\n+.BR \\-u \", \" \\-\\-update\n+Update metadata if CRC validation fails. Useful for recovering from corrupted\n+metadata.\n+.\n+.TP\n+.BR \\-a \", \" \\-\\-active \" \\fIbankid\\fR\"\n+Set the active bank index to\n+.IR bank .\n+.\n+.TP\n+.BR \\-p \", \" \\-\\-previous \" \\fIbankid\\fR\"\n+Set the previous active bank index to\n+.IR bank .\n+.\n+.TP\n+.BR \\-s \", \" \\-\\-state \" \\fIbankid state\\fR\"\n+Set bank index\n+.I bankid\n+to the specified\n+.IR state .\n+Valid states are:\n+.BR accepted ,\n+.BR valid ,\n+or\n+.BR invalid .\n+Supported only with version 2 metadata. When setting a bank to accepted state,\n+all firmware images in that bank are automatically marked as accepted.\n+.\n+.TP\n+.BR \\-i \", \" \\-\\-image \" \\fIimageid\\fR\"\n+Specify image number (used with\n+.B \\-A\n+or\n+.BR \\-C ).\n+.\n+.TP\n+.BR \\-b \", \" \\-\\-bank \" \\fIbankid\\fR\"\n+Specify bank number (used with\n+.B \\-A\n+or\n+.BR \\-C ).\n+.\n+.TP\n+.BR \\-A \", \" \\-\\-accept\n+Accept the image specified by\n+.B \\-i\n+in the bank specified by\n+.BR \\-b .\n+Sets the FWU_IMAGE_ACCEPTED flag for the image.\n+.\n+.TP\n+.BR \\-C \", \" \\-\\-clear\n+Clear the acceptance flag for the image specified by\n+.B \\-i\n+in the bank specified by\n+.BR \\-b .\n+According to the FWU specification, the bank state is automatically set to\n+invalid before clearing the acceptance flag.\n+.\n+.TP\n+.BR \\-B \", \" \\-\\-nbanks \" \\fInum_banks\\fR\"\n+Specify total number of banks (required for V1 metadata).\n+.\n+.TP\n+.BR \\-I \", \" \\-\\-nimages \" \\fInum_images\\fR\"\n+Specify total number of images (required for V1 metadata).\n+.\n+.TP\n+.BR \\-h \", \" \\-\\-help\n+Print usage information and exit.\n+.\n+.SH CONFIGURATION FILE\n+The configuration file specifies the location of FWU metadata on storage\n+devices. The format is:\n+.PP\n+.EX\n+.in +4\n+# Device Name      Device Offset    Metadata Size    Erase Size\n+/dev/mtd0          0x0              0x78             0x1000\n+/dev/mtd1          0x0              0x78             0x1000\n+.in\n+.EE\n+.PP\n+Lines starting with\n+.B #\n+are comments.\n+.I Erase Size\n+is optional and only applies to MTD devices; if omitted, it defaults to the\n+metadata size.\n+.PP\n+Specifying two devices enables redundant metadata support.\n+.\n+.SH BUGS\n+Please report bugs to the\n+.UR https://\\:source\\:.denx\\:.de/\\:u-boot/\\:u-boot/\\:issues\n+U-Boot bug tracker\n+.UE .\n+.\n+.SH EXAMPLES\n+Display FWU metadata summary:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata\n+.in\n+.EE\n+.PP\n+Display detailed metadata with all GUIDs:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-l\n+.in\n+.EE\n+.PP\n+Set active bank to 1:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-a 1\n+.in\n+.EE\n+.PP\n+Set bank 1 to accepted state (automatically accepts all images in that bank):\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-s 1 accepted\n+.in\n+.EE\n+.PP\n+Accept image 0 in bank 0:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-i 0 \\-b 0 \\-A \\-l\n+.in\n+.EE\n+.PP\n+Clear acceptance for image 0 in bank 1:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-i 0 \\-b 1 \\-C \\-l\n+.in\n+.EE\n+.PP\n+Clear acceptance for image 1 in bank 1 with metadata V1:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-B 2 \\-I 2 \\-i 1 \\-b 1 \\-C \\-l\n+.in\n+.EE\n+.PP\n+Use custom configuration file:\n+.PP\n+.EX\n+.in +4\n+$ \\c\n+.B fwumdata \\-c /path/to/custom.config\n+.in\n+.EE\n+.\n+.SH SEE ALSO\n+.BR mkfwumdata (1)\ndiff --git a/tools/.gitignore b/tools/.gitignore\nindex e8daa24a52d..49943d2cf3a 100644\n--- a/tools/.gitignore\n+++ b/tools/.gitignore\n@@ -11,6 +11,7 @@\n /file2include\n /fit_check_sign\n /fit_info\n+/fwumdata\n /gdb/gdbcont\n /gdb/gdbsend\n /gen_eth_addr\ndiff --git a/tools/fwumdata_src/Kconfig b/tools/fwumdata_src/Kconfig\nindex c033c560e8d..af1f3bb3f57 100644\n--- a/tools/fwumdata_src/Kconfig\n+++ b/tools/fwumdata_src/Kconfig\n@@ -6,3 +6,14 @@ config TOOLS_MKFWUMDATA\n \t  metadata for initial installation of the FWU multi bank\n \t  update on the board. The installation method depends on\n \t  the platform.\n+\n+config TOOLS_FWUMDATA\n+\tbool \"Build fwumdata command\"\n+\tdefault y if FWU_MULTI_BANK_UPDATE\n+\thelp\n+\t  This command allows users to read, display, and modify FWU\n+\t  (Firmware Update) metadata from Linux userspace. It provides\n+\t  functionality similar to fw_printenv/fw_setenv but for FWU\n+\t  metadata. Users can view metadata, change active/previous\n+\t  bank indices, modify bank states, and set image acceptance\n+\t  flags. Configuration is done via fwumdata.config file.\ndiff --git a/tools/fwumdata_src/fwumdata.c b/tools/fwumdata_src/fwumdata.c\nnew file mode 100644\nindex 00000000000..c5b0f56842d\n--- /dev/null\n+++ b/tools/fwumdata_src/fwumdata.c\n@@ -0,0 +1,854 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * FWU Metadata Read/Write Tool\n+ * Copyright (c) 2025, Kory Maincent <kory.maincent@bootlin.com>\n+ *\n+ * Tool to read, display, and modify FWU (Firmware Update) metadata\n+ * from Linux userspace. Similar to fw_printenv/fw_setenv for U-Boot\n+ * environment, but for FWU metadata.\n+ *\n+ * Usage:\n+ *   fwumdata                          - Print all metadata\n+ *   fwumdata -u                       - Print metadata and update it if CRC corrupted\n+ *   fwumdata -c <config>              - Use custom config file\n+ *   fwumdata -a <bank>                - Set active bank\n+ *   fwumdata -p <bank>                - Set previous bank\n+ *   fwumdata -s <bank> <state>        - Set bank state (V2 only)\n+ *   fwumdata -i <id> -b <bank> -A     - Accept image\n+ *   fwumdata -i <id> -b <bank> -C     - Clear image acceptance\n+ *   fwumdata -i <id> -b <bank>\n+ *            -B <num_banks>\n+ *            -I <num_images> -C       - Clear image acceptance (V1 only)\n+ *   fwumdata -l                       - List detailed info with GUIDs\n+ */\n+\n+#include <errno.h>\n+#include <getopt.h>\n+#include <stdio.h>\n+#include <unistd.h>\n+#include <mtd/mtd-user.h>\n+#include <sys/ioctl.h>\n+#include <u-boot/crc.h>\n+#include \"fwumdata.h\"\n+\n+/* Device configuration */\n+struct fwumdata_device {\n+\tconst char *devname;\n+\tlong long devoff;\n+\tunsigned long mdata_size;\n+\tunsigned long erase_size;\n+\tint fd;\n+\tbool is_mtd;\n+};\n+\n+/* Global state */\n+static struct fwumdata_device devices[2];  /* Primary and secondary */\n+static struct fwu_mdata *mdata;\n+static int have_redundant;\n+static struct fwu_mdata *valid_mdata;\n+static bool mdata_mod;\n+static const char *config_file;\n+static int nbanks, nimages; /* For V1 only */\n+static const char * const default_config_files[] = {\n+\t\"./fwumdata.config\",\n+\t\"/etc/fwumdata.config\",\n+\tNULL\n+};\n+\n+/* GUID/UUID utilities */\n+static void guid_to_string(const struct efi_guid *guid, char *str)\n+{\n+\tsprintf(str, \"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n+\t\tguid->time_high, guid->time_low, guid->reserved,\n+\t\tguid->family, guid->node[0],\n+\t\tguid->node[1], guid->node[2], guid->node[3],\n+\t\tguid->node[4], guid->node[5], guid->node[6]);\n+}\n+\n+/* Config file parsing */\n+static int parse_config(const char *fname)\n+{\n+\tsize_t linesize = 0;\n+\tchar *line = NULL;\n+\tchar *devname;\n+\tint i = 0;\n+\tFILE *fp;\n+\tint rc;\n+\n+\tfp = fopen(fname, \"r\");\n+\tif (!fp)\n+\t\treturn -ENOENT;\n+\n+\twhile (i < 2 && getline(&line, &linesize, fp) != -1) {\n+\t\t/* Skip comments and empty lines */\n+\t\tif (line[0] == '#' || line[0] == '\\n')\n+\t\t\tcontinue;\n+\n+\t\trc = sscanf(line, \"%ms %lli %lx %lx\",\n+\t\t\t    &devname,\n+\t\t\t    &devices[i].devoff,\n+\t\t\t    &devices[i].mdata_size,\n+\t\t\t    &devices[i].erase_size);\n+\n+\t\tif (rc < 3) {\n+\t\t\tfree(devname);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (rc < 4)\n+\t\t\tdevices[i].erase_size = devices[i].mdata_size;\n+\n+\t\tdevices[i].devname = devname;\n+\t\ti++;\n+\t}\n+\n+\tfree(line);\n+\tfclose(fp);\n+\n+\tif (i == 2) {\n+\t\thave_redundant = true;\n+\t\tif (devices[0].mdata_size != devices[1].mdata_size) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Size mismatch between the two metadata\\n\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\tif (!i) {\n+\t\tfprintf(stderr,\n+\t\t\t\"Can't read config %s content\\n\", fname);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int find_parse_config(void)\n+{\n+\tint i;\n+\n+\tif (config_file)\n+\t\treturn parse_config(config_file);\n+\n+\tfor (i = 0; default_config_files[i]; i++) {\n+\t\tint ret;\n+\n+\t\tret = parse_config(default_config_files[i]);\n+\t\tif (ret == -ENOENT)\n+\t\t\tcontinue;\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tconfig_file = default_config_files[i];\n+\t\treturn 0;\n+\t}\n+\n+\tfprintf(stderr, \"Error: Cannot find config file\\n\");\n+\treturn -ENOENT;\n+}\n+\n+static int open_device(struct fwumdata_device *dev)\n+{\n+\tif (strstr(dev->devname, \"/dev/mtd\"))\n+\t\tdev->is_mtd = true;\n+\n+\tdev->fd = open(dev->devname, O_RDWR | O_SYNC);\n+\tif (dev->fd < 0) {\n+\t\tfprintf(stderr, \"Cannot open %s: %s\\n\", dev->devname,\n+\t\t\tstrerror(errno));\n+\t\treturn -ENODEV;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int mtd_erase(int fd, unsigned long offset, unsigned long size)\n+{\n+\tstruct erase_info_user erase;\n+\tint ret;\n+\n+\terase.start = offset;\n+\terase.length = size;\n+\n+\tret = ioctl(fd, MEMERASE, &erase);\n+\tif (ret < 0) {\n+\t\tfprintf(stderr, \"MTD erase failed: %s\\n\", strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int read_device(struct fwumdata_device *dev, void *buf, size_t count)\n+{\n+\tif (lseek(dev->fd, dev->devoff, SEEK_SET) < 0) {\n+\t\tfprintf(stderr, \"Seek failed: %s\\n\", strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\tif (read(dev->fd, buf, count) < 0) {\n+\t\tfprintf(stderr, \"Read failed: %s\\n\", strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int write_device(struct fwumdata_device *dev, const void *buf,\n+\t\t\tsize_t count)\n+{\n+\tint ret;\n+\n+\t/* Erase if MTD device */\n+\tif (dev->is_mtd) {\n+\t\tret = mtd_erase(dev->fd, dev->devoff, dev->erase_size);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (lseek(dev->fd, dev->devoff, SEEK_SET) < 0) {\n+\t\tfprintf(stderr, \"Seek failed: %s\\n\", strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\tif (write(dev->fd, buf, count) < 0) {\n+\t\tfprintf(stderr, \"Write failed: %s\\n\", strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Metadata operations */\n+static int validate_crc(struct fwu_mdata *mdata, size_t size)\n+{\n+\tu32 calc_crc, stored_crc;\n+\n+\tstored_crc = mdata->crc32;\n+\tcalc_crc = crc32(0, (const u8 *)&mdata->version, size - sizeof(u32));\n+\n+\tif (calc_crc != stored_crc) {\n+\t\tfprintf(stderr,\n+\t\t\t\"CRC mismatch: calculated 0x%08x, stored 0x%08x\\n\",\n+\t\t\tcalc_crc, stored_crc);\n+\t\tif (mdata->version == 1)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Metadata is V1, this may be size description issue\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void update_crc(struct fwu_mdata *mdata, size_t size)\n+{\n+\tmdata->crc32 = crc32(0, (const u8 *)&mdata->version, size - sizeof(u32));\n+}\n+\n+static int read_one_metadata(int mdata_id, size_t size)\n+{\n+\tint ret;\n+\n+\tret = open_device(&devices[mdata_id]);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = read_device(&devices[mdata_id], &mdata[mdata_id], size);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (mdata[mdata_id].version != 1 && mdata[mdata_id].version != 2) {\n+\t\tfprintf(stderr, \"Invalid metadata %d version: %u\\n\",\n+\t\t\tmdata_id, mdata[mdata_id].version);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int read_metadata(bool update)\n+{\n+\tsize_t alloc_size;\n+\tint ret;\n+\n+\t/* Allocate initial buffer */\n+\talloc_size = devices[0].mdata_size;\n+\tmdata = calloc(have_redundant ? 2 : 1, alloc_size);\n+\tif (!mdata) {\n+\t\tfprintf(stderr, \"Memory allocation failed\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tret = read_one_metadata(0, alloc_size);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (validate_crc(&mdata[0], alloc_size) < 0) {\n+\t\tfprintf(stderr,\n+\t\t\t\"Warning: Primary metadata CRC validation failed\\n\");\n+\t\tmdata_mod = update;\n+\t} else {\n+\t\tvalid_mdata = &mdata[0];\n+\t}\n+\n+\tif (have_redundant) {\n+\t\tret = read_one_metadata(1, alloc_size);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (validate_crc(&mdata[1], alloc_size) < 0) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Warning: Secondary metadata CRC validation failed\\n\");\n+\t\t\tmdata_mod = update;\n+\t\t} else if (valid_mdata && mdata[0].crc32 != mdata[1].crc32) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Metadatas valid but not equal, use first one as default\\n\");\n+\t\t\tmdata_mod = update;\n+\t\t} else {\n+\t\t\tvalid_mdata = &mdata[1];\n+\t\t}\n+\t}\n+\n+\tif (!valid_mdata) {\n+\t\tfprintf(stderr,\n+\t\t\t\"No metadata valid, use first one as default\\n\");\n+\t\tmdata_mod = update;\n+\t\tvalid_mdata = &mdata[0];\n+\t}\n+\n+\tif (valid_mdata->version == 2) {\n+\t\tstruct fwu_mdata_ext *mdata_ext;\n+\n+\t\tmdata_ext = fwu_get_fw_mdata_ext(valid_mdata);\n+\t\tif (mdata_ext->metadata_size != alloc_size) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Metadata real size 0x%x mismatch with the config 0x%zx\\n\",\n+\t\t\t\tmdata_ext->metadata_size, alloc_size);\n+\t\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int write_metadata(void)\n+{\n+\tsize_t write_size = devices[0].mdata_size;\n+\tint ret;\n+\n+\tif (!mdata_mod)\n+\t\treturn 0;\n+\n+\t/* Update CRC */\n+\tupdate_crc(valid_mdata, write_size);\n+\n+\t/* Write primary */\n+\tret = write_device(&devices[0], valid_mdata, write_size);\n+\tif (ret < 0) {\n+\t\tfprintf(stderr, \"Failed to write primary metadata\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\t/* Write secondary if redundant */\n+\tif (have_redundant) {\n+\t\tret = write_device(&devices[1], valid_mdata, write_size);\n+\t\tif (ret < 0) {\n+\t\t\tfprintf(stderr, \"Failed to write secondary metadata\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\tprintf(\"FWU metadata updated successfully\\n\");\n+\tmdata_mod = 0;\n+\n+\treturn 0;\n+}\n+\n+/* Display functions */\n+static const char *bank_state_to_string(u8 state)\n+{\n+\tswitch (state) {\n+\tcase FWU_BANK_ACCEPTED:\n+\t\treturn \"accepted\";\n+\tcase FWU_BANK_VALID:\n+\t\treturn \"valid\";\n+\tcase FWU_BANK_INVALID:\n+\t\treturn \"invalid\";\n+\tdefault:\n+\t\treturn \"unknown\";\n+\t}\n+}\n+\n+static void print_metadata_summary(void)\n+{\n+\tint i;\n+\n+\tprintf(\"FWU Metadata:\\n\");\n+\tprintf(\"\\tVersion:            %u\\n\", valid_mdata->version);\n+\tprintf(\"\\tActive Index:       %u\\n\", valid_mdata->active_index);\n+\tprintf(\"\\tPrevious Index:     %u\\n\", valid_mdata->previous_active_index);\n+\tprintf(\"\\tCRC32:              0x%08x\\n\", valid_mdata->crc32);\n+\n+\tif (valid_mdata->version == 2) {\n+\t\tstruct fwu_fw_store_desc *fw_desc;\n+\t\tstruct fwu_mdata_ext *mdata_ext;\n+\n+\t\tmdata_ext = fwu_get_fw_mdata_ext(valid_mdata);\n+\t\tprintf(\"\\tMetadata Size:      %u bytes\\n\", mdata_ext->metadata_size);\n+\t\tprintf(\"\\tDescriptor Offset:  %u\\n\", mdata_ext->desc_offset);\n+\t\tprintf(\"\\tBank States:\\n\");\n+\n+\t\tfw_desc = fwu_get_fw_desc(valid_mdata);\n+\t\tfor (i = 0; i < fw_desc->num_banks && i < MAX_BANKS_V2; i++) {\n+\t\t\tprintf(\"\\t\\tBank %d: %s (0x%02x)\\n\", i,\n+\t\t\t       bank_state_to_string(mdata_ext->bank_state[i]),\n+\t\t\t       mdata_ext->bank_state[i]);\n+\t\t}\n+\t}\n+}\n+\n+static void print_metadata_detailed(void)\n+{\n+\tstruct fwu_fw_store_desc *fw_desc = NULL;\n+\tstruct fwu_image_bank_info *bank_info;\n+\tstruct fwu_image_entry *img_entry;\n+\tint num_images, num_banks;\n+\tchar guid_str[64];\n+\tint i, j;\n+\n+\tprint_metadata_summary();\n+\n+\tif (valid_mdata->version == 1) {\n+\t\tnum_images = nimages;\n+\t\tnum_banks = nbanks;\n+\t} else {\n+\t\tfw_desc = fwu_get_fw_desc(valid_mdata);\n+\t\tnum_images = fw_desc->num_images;\n+\t\tnum_banks = fw_desc->num_banks;\n+\t}\n+\n+\tif (fw_desc) {\n+\t\tprintf(\"\\n\\tFirmware Store Descriptor:\\n\");\n+\t\tprintf(\"\\t\\tNumber of Banks:       %u\\n\", num_banks);\n+\t\tprintf(\"\\t\\tNumber of Images:      %u\\n\", num_images);\n+\t\tprintf(\"\\t\\tImage Entry Size:      %u\\n\", fw_desc->img_entry_size);\n+\t\tprintf(\"\\t\\tBank Info Entry Size:  %u\\n\", fw_desc->bank_info_entry_size);\n+\t}\n+\n+\tprintf(\"\\n\\tImages:\\n\");\n+\tfor (i = 0; i < num_images; i++) {\n+\t\timg_entry = fwu_get_image_entry(valid_mdata, valid_mdata->version,\n+\t\t\t\t\t\tnum_banks, i);\n+\n+\t\tprintf(\"\\t\\tImage %d:\\n\", i);\n+\n+\t\tguid_to_string(&img_entry->image_type_guid, guid_str);\n+\t\tprintf(\"\\t\\t\\tImage Type GUID:  %s\\n\", guid_str);\n+\n+\t\tguid_to_string(&img_entry->location_guid, guid_str);\n+\t\tprintf(\"\\t\\t\\tLocation GUID:    %s\\n\", guid_str);\n+\n+\t\tprintf(\"\\t\\t\\tBanks:\\n\");\n+\t\tfor (j = 0; j < num_banks; j++) {\n+\t\t\tbank_info = fwu_get_bank_info(valid_mdata,\n+\t\t\t\t\t\t      valid_mdata->version,\n+\t\t\t\t\t\t      num_banks, i, j);\n+\n+\t\t\tguid_to_string(&bank_info->image_guid, guid_str);\n+\t\t\tprintf(\"\\t\\t\\t\\tBank %d:\\n\", j);\n+\t\t\tprintf(\"\\t\\t\\t\\t\\tImage GUID:  %s\\n\", guid_str);\n+\t\t\tprintf(\"\\t\\t\\t\\t\\tAccepted:    %s (%u)\\n\",\n+\t\t\t       (bank_info->accepted & FWU_IMAGE_ACCEPTED) ? \"yes\" : \"no\",\n+\t\t\t       bank_info->accepted);\n+\t\t}\n+\t}\n+}\n+\n+/* Modification functions */\n+static int set_active_index(int bank)\n+{\n+\tstruct fwu_fw_store_desc *fw_desc;\n+\tint num_banks;\n+\n+\tif (valid_mdata->version == 2) {\n+\t\tfw_desc = fwu_get_fw_desc(valid_mdata);\n+\t\tnum_banks = fw_desc->num_banks;\n+\t} else {\n+\t\tnum_banks = nbanks;\n+\t}\n+\n+\tif (bank < 0 || bank >= num_banks) {\n+\t\tfprintf(stderr, \"Error: Invalid bank %d (must be 0-%d)\\n\",\n+\t\t\tbank, num_banks - 1);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (valid_mdata->active_index == bank)\n+\t\treturn 0;\n+\n+\tvalid_mdata->active_index = bank;\n+\tmdata_mod = 1;\n+\n+\tprintf(\"Active bank set to %d\\n\", bank);\n+\treturn 0;\n+}\n+\n+static int set_previous_index(int bank)\n+{\n+\tstruct fwu_fw_store_desc *fw_desc;\n+\tint num_banks;\n+\n+\tif (valid_mdata->version == 2) {\n+\t\tfw_desc = fwu_get_fw_desc(valid_mdata);\n+\t\tnum_banks = fw_desc->num_banks;\n+\t} else {\n+\t\tnum_banks = nbanks;\n+\t}\n+\n+\tif (bank < 0 || bank >= num_banks) {\n+\t\tfprintf(stderr, \"Error: Invalid bank %d (must be 0-%d)\\n\",\n+\t\t\tbank, num_banks - 1);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (valid_mdata->previous_active_index == bank)\n+\t\treturn 0;\n+\n+\tvalid_mdata->previous_active_index = bank;\n+\tmdata_mod = 1;\n+\n+\tprintf(\"Previous bank set to %d\\n\", bank);\n+\treturn 0;\n+}\n+\n+static int set_image_accepted(int image, int bank, int accept)\n+{\n+\tstruct fwu_image_bank_info *bank_info;\n+\tint num_images, num_banks;\n+\n+\tif (valid_mdata->version == 1) {\n+\t\tnum_images = nimages;\n+\t\tnum_banks = nbanks;\n+\t} else {\n+\t\tstruct fwu_fw_store_desc *fw_desc;\n+\n+\t\tfw_desc = fwu_get_fw_desc(valid_mdata);\n+\t\tnum_images = fw_desc->num_images;\n+\t\tnum_banks = fw_desc->num_banks;\n+\t}\n+\n+\tif (bank < 0 || bank >= num_banks) {\n+\t\tfprintf(stderr, \"Error: Invalid bank %d (must be 0-%d)\\n\",\n+\t\t\tbank, num_banks - 1);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (image < 0 || image >= num_images) {\n+\t\tfprintf(stderr, \"Error: Invalid image %d (must be 0-%d)\\n\",\n+\t\t\timage, num_images - 1);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tbank_info = fwu_get_bank_info(valid_mdata, valid_mdata->version,\n+\t\t\t\t      num_banks, image, bank);\n+\tif (accept == bank_info->accepted)\n+\t\treturn 0;\n+\n+\tif (accept) {\n+\t\tbank_info->accepted = FWU_IMAGE_ACCEPTED;\n+\t} else {\n+\t\tbank_info->accepted = 0;\n+\n+\t\t/* According to the spec: bank_state[index] have to be set\n+\t\t * to invalid before any content in the img_bank_info[index]\n+\t\t * is overwritten.\n+\t\t */\n+\t\tif (valid_mdata->version == 2) {\n+\t\t\tstruct fwu_mdata_ext *mdata_ext;\n+\n+\t\t\tmdata_ext = fwu_get_fw_mdata_ext(valid_mdata);\n+\t\t\tmdata_ext->bank_state[bank] = FWU_BANK_INVALID;\n+\t\t}\n+\t}\n+\n+\tmdata_mod = 1;\n+\tprintf(\"Image %d in bank %d: acceptance %s\\n\",\n+\t       image, bank, accept ? \"set\" : \"cleared\");\n+\n+\treturn 0;\n+}\n+\n+static int set_bank_state(int bank, const char *state_str)\n+{\n+\tstruct fwu_fw_store_desc *fw_desc;\n+\tstruct fwu_mdata_ext *mdata_ext;\n+\tu8 state;\n+\tint i;\n+\n+\tif (valid_mdata->version != 2) {\n+\t\tfprintf(stderr,\n+\t\t\t\"Error: Bank state is only supported in V2 metadata\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfw_desc = fwu_get_fw_desc(valid_mdata);\n+\tmdata_ext = fwu_get_fw_mdata_ext(valid_mdata);\n+\n+\tif (bank < 0 || bank >= fw_desc->num_banks || bank >= MAX_BANKS_V2) {\n+\t\tfprintf(stderr, \"Error: Invalid bank %d (must be 0-%d)\\n\",\n+\t\t\tbank, fw_desc->num_banks - 1);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Parse state string */\n+\tif (!strcmp(state_str, \"accepted\")) {\n+\t\tstate = FWU_BANK_ACCEPTED;\n+\t} else if (!strcmp(state_str, \"valid\")) {\n+\t\tstate = FWU_BANK_VALID;\n+\t} else if (!strcmp(state_str, \"invalid\")) {\n+\t\tstate = FWU_BANK_INVALID;\n+\t} else {\n+\t\tfprintf(stderr,\n+\t\t\t\"Error: Invalid state '%s' (must be accepted/valid/invalid)\\n\",\n+\t\t\tstate_str);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (mdata_ext->bank_state[bank] == state)\n+\t\treturn 0;\n+\n+\t/* If a bank is set in a accepted state all firmware images in\n+\t * that bank must be marked as accepted as described in the spec.\n+\t */\n+\tif (state == FWU_BANK_ACCEPTED) {\n+\t\tfor (i = 0; i < fw_desc->num_images; i++) {\n+\t\t\tint ret;\n+\n+\t\t\tret = set_image_accepted(i, bank, true);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tmdata_ext->bank_state[bank] = state;\n+\tmdata_mod = 1;\n+\n+\tprintf(\"Bank %d state set to %s (0x%02x)\\n\", bank, state_str, state);\n+\treturn 0;\n+}\n+\n+static int metadata_v1_validate_size(void)\n+{\n+\tint calc_size;\n+\n+\tcalc_size = sizeof(struct fwu_mdata) +\n+\t\t    (sizeof(struct fwu_image_entry) +\n+\t\t     sizeof(struct fwu_image_bank_info) * nbanks) * nimages;\n+\n+\tif (devices[0].mdata_size != calc_size) {\n+\t\tfprintf(stderr,\n+\t\t\t\"Metadata calculate size (-B and -I options) 0x%x mismatch with the config 0x%zx\\n\",\n+\t\t\tcalc_size, devices[0].mdata_size);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Command-line interface */\n+static void print_usage(void)\n+{\n+\tfprintf(stderr, \"Usage: fwumdata [options]\\n\\n\");\n+\tfprintf(stderr, \"Options:\\n\"\n+\t\t\"\\t-c, --config <file>         Use custom config file, defaults:\\n\"\n+\t\t\"\\t                              ./fwumdata.config or /etc/fwumdata.config\\n\"\n+\t\t\"\\t-l, --list                  List detailed metadata with GUIDs\\n\"\n+\t\t\"\\t-a, --active <bank>         Set active bank index\\n\"\n+\t\t\"\\t-p, --previous <bank>       Set previous bank index\\n\"\n+\t\t\"\\t-s, --state <bank> <state>  Set bank state (V2 only)\\n\"\n+\t\t\"\\t                              state: accepted|valid|invalid\\n\"\n+\t\t\"\\t-i, --image <id>            Image number (for -A/-C)\\n\"\n+\t\t\"\\t-b, --bank <bank>           Bank number (for -A/-C)\\n\"\n+\t\t\"\\t-A, --accept                Accept image (requires -i and -b)\\n\"\n+\t\t\"\\t-C, --clear                 Clear image acceptance (requires -i and -b)\\n\"\n+\t\t\"\\t-u, --update                Update metadata if there is a checksum issue\\n\"\n+\t\t\"\\t-B, --nbanks <num_banks>    Number of banks (required for V1 metadata)\\n\"\n+\t\t\"\\t-I, --nimages <num_images>  Number of images (required for V1 metadata)\\n\"\n+\t\t\"\\t-h, --help                  Print this help\\n\\n\");\n+\tfprintf(stderr, \"Config file format (fwumdata.config):\\n\"\n+\t\t\"\\t# Device Name      Device Offset    Metadata Size    Erase Size\\n\"\n+\t\t\"\\t/dev/mtd0          0x0              0x78             0x1000\\n\"\n+\t\t\"\\t/dev/mtd1          0x0              0x78             0x1000\\n\\n\");\n+\tfprintf(stderr, \"Examples:\\n\"\n+\t\t\"\\tfwumdata                              # Print metadata summary\\n\"\n+\t\t\"\\tfwumdata -l                           # Print detailed metadata\\n\"\n+\t\t\"\\tfwumdata -a 1                         # Set active bank to 1\\n\"\n+\t\t\"\\tfwumdata -s 1 accepted                # Set bank 1 to accepted state\\n\"\n+\t\t\"\\tfwumdata -i 0 -b 0 -A                 # Accept image in bank 0\\n\"\n+\t\t\"\\tfwumdata -B 2 -I 2 -i 1 -b 1 -A -l    # Accept image 1 in bank 1 with metadata V1\\n\");\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tchar *bank_state_str = NULL;\n+\tbool list_detailed = false;\n+\tint bank_state_num = -1;\n+\tint active_index = -1;\n+\tint bank_id = -1;\n+\tint prev_index = -1;\n+\tbool do_accept = 0;\n+\tbool do_clear = 0;\n+\tbool do_update = 0;\n+\tint image_id = -1;\n+\tint ret = 0;\n+\tint opt;\n+\n+\tstatic struct option long_options[] = {\n+\t\t{\"config\", required_argument, 0, 'c'},\n+\t\t{\"list\", no_argument, 0, 'l'},\n+\t\t{\"active\", required_argument, 0, 'a'},\n+\t\t{\"previous\", required_argument, 0, 'p'},\n+\t\t{\"state\", required_argument, 0, 's'},\n+\t\t{\"image\", required_argument, 0, 'i'},\n+\t\t{\"bank\", required_argument, 0, 'b'},\n+\t\t{\"accept\", no_argument, 0, 'A'},\n+\t\t{\"clear\", no_argument, 0, 'C'},\n+\t\t{\"update\", no_argument, 0, 'u'},\n+\t\t{\"nbanks\", required_argument, 0, 'B'},\n+\t\t{\"nimages\", required_argument, 0, 'I'},\n+\t\t{\"help\", no_argument, 0, 'h'},\n+\t\t{0, 0, 0, 0}\n+\t};\n+\n+\t/* Parse arguments */\n+\twhile ((opt = getopt_long(argc, argv, \"c:la:p:s:i:b:ACuB:I:h\", long_options, NULL)) != -1) {\n+\t\tswitch (opt) {\n+\t\tcase 'c':\n+\t\t\tconfig_file = optarg;\n+\t\t\tbreak;\n+\t\tcase 'l':\n+\t\t\tlist_detailed = 1;\n+\t\t\tbreak;\n+\t\tcase 'a':\n+\t\t\tactive_index = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 'p':\n+\t\t\tprev_index = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 's':\n+\t\t\tbank_state_num = atoi(optarg);\n+\t\t\tif (optind < argc && argv[optind][0] != '-') {\n+\t\t\t\tbank_state_str = argv[optind++];\n+\t\t\t} else {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"Error: -s requires bank number and state\\n\");\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase 'i':\n+\t\t\timage_id = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 'b':\n+\t\t\tbank_id = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 'A':\n+\t\t\tdo_accept = 1;\n+\t\t\tbreak;\n+\t\tcase 'C':\n+\t\t\tdo_clear = 1;\n+\t\t\tbreak;\n+\t\tcase 'u':\n+\t\t\tdo_update = 1;\n+\t\t\tbreak;\n+\t\tcase 'B':\n+\t\t\tnbanks = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 'I':\n+\t\t\tnimages = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 'h':\n+\t\t\tprint_usage();\n+\t\t\treturn 0;\n+\t\tdefault:\n+\t\t\tprint_usage();\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\n+\tret = find_parse_config();\n+\tif (ret < 0) {\n+\t\tfprintf(stderr, \"Error: Cannot read configuration\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = read_metadata(do_update);\n+\tif (ret < 0) {\n+\t\tfprintf(stderr, \"Error: Cannot read metadata\\n\");\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (valid_mdata->version == 1) {\n+\t\tret = metadata_v1_validate_size();\n+\t\tif (ret)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\t/* Perform operations */\n+\tif (active_index >= 0) {\n+\t\tret = set_active_index(active_index);\n+\t\tif (ret < 0)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif (prev_index >= 0) {\n+\t\tret = set_previous_index(prev_index);\n+\t\tif (ret < 0)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif (do_accept || do_clear) {\n+\t\tif (image_id < 0 || bank_id < 0) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Error: -A/-C requires both -i <guid> and -b <bank>\\n\");\n+\t\t\tret = -EINVAL;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tret = set_image_accepted(image_id, bank_id, do_accept);\n+\t\tif (ret < 0)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tif (bank_state_num >= 0 && bank_state_str) {\n+\t\tret = set_bank_state(bank_state_num, bank_state_str);\n+\t\tif (ret < 0)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\t/* Write back if modified */\n+\tif (mdata_mod) {\n+\t\tret = write_metadata();\n+\t\tif (ret)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\t/* Display metadata if no modifications or list requested */\n+\tif (list_detailed)\n+\t\tprint_metadata_detailed();\n+\telse\n+\t\tprint_metadata_summary();\n+\n+cleanup:\n+\t/* Close devices and free memory */\n+\tif (devices[0].fd)\n+\t\tclose(devices[0].fd);\n+\tif (devices[1].fd)\n+\t\tclose(devices[1].fd);\n+\n+\tfree(mdata);\n+\n+\tfor (int i = 0; i < 2; i++) {\n+\t\tif (devices[i].devname)\n+\t\t\tfree((void *)devices[i].devname);\n+\t}\n+\n+\treturn ret;\n+}\ndiff --git a/tools/fwumdata_src/fwumdata.config b/tools/fwumdata_src/fwumdata.config\nnew file mode 100644\nindex 00000000000..7e83f7a5909\n--- /dev/null\n+++ b/tools/fwumdata_src/fwumdata.config\n@@ -0,0 +1,33 @@\n+# FWU Metadata Configuration File\n+#\n+# Format: <device> <offset> <metadata_size> <erase_size>\n+#\n+# This file describes where the FWU metadata is stored. You can specify\n+# up to two entries for redundant metadata copies.\n+#\n+# Device: MTD device (/dev/mtdX), block device (/dev/mmcblkX), or file path\n+# Offset: Byte offset from start of device (hex with 0x prefix)\n+# Metadata Size: Size of metadata structure in bytes (hex with 0x prefix)\n+# Erase Size: Sector/erase block size (hex with 0x prefix, defaults to\n+#\t      metadata_size, required only for MTD device)\n+#\n+# Examples:\n+#\n+# MTD devices (NOR/NAND flash):\n+# /dev/mtd0  0x0       0x1000  0x1000\n+# /dev/mtd1  0x0       0x1000  0x1000\n+#\n+# Block device (eMMC/SD):\n+# /dev/mmcblk0  0x100000  0x78\n+# /dev/mmcblk0  0x101000  0x78\n+#\n+# or:\n+# /dev/disk/by-partlabel/metadata1  0  0x78\n+# /dev/disk/by-partlabel/metadata2  0  0x78\n+#\n+# Regular file:\n+# /boot/fwu-mdata.bin  0x0  0x78\n+#\n+# Default configuration (update for your platform):\n+/dev/mtd0  0x0  0x78  0x1000\n+/dev/mtd1  0x0  0x78  0x1000\ndiff --git a/tools/fwumdata_src/fwumdata.h b/tools/fwumdata_src/fwumdata.h\nnew file mode 100644\nindex 00000000000..5e2c45d0fb0\n--- /dev/null\n+++ b/tools/fwumdata_src/fwumdata.h\n@@ -0,0 +1,138 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2025, Kory Maincent <kory.maincent@bootlin.com>\n+ */\n+\n+#ifndef _FWUMDATA_H_\n+#define _FWUMDATA_H_\n+\n+#include <linux/compiler_attributes.h>\n+\n+/* Type definitions for U-Boot compatibility */\n+typedef uint8_t u8;\n+typedef uint16_t u16;\n+typedef uint32_t u32;\n+typedef uint64_t u64;\n+\n+/* FWU Constants */\n+#define FWU_IMAGE_ACCEPTED\t0x1\n+#define FWU_BANK_INVALID\t(uint8_t)0xFF\n+#define FWU_BANK_VALID\t\t(uint8_t)0xFE\n+#define FWU_BANK_ACCEPTED\t(uint8_t)0xFC\n+#define MAX_BANKS_V2\t\t4\n+\n+/* EFI GUID structure */\n+struct efi_guid {\n+\tu32 time_high;\n+\tu16 time_low;\n+\tu16 reserved;\n+\tu8  family;\n+\tu8  node[7];\n+} __packed;\n+\n+/* FWU Metadata structures */\n+struct fwu_image_bank_info {\n+\tstruct efi_guid  image_guid;\n+\tu32 accepted;\n+\tu32 reserved;\n+} __packed;\n+\n+struct fwu_image_entry {\n+\tstruct efi_guid image_type_guid;\n+\tstruct efi_guid location_guid;\n+\tstruct fwu_image_bank_info img_bank_info[0]; /* Variable length */\n+} __packed;\n+\n+struct fwu_fw_store_desc {\n+\tu8  num_banks;\n+\tu8  reserved;\n+\tu16 num_images;\n+\tu16 img_entry_size;\n+\tu16 bank_info_entry_size;\n+\tstruct fwu_image_entry img_entry[0]; /* Variable length */\n+} __packed;\n+\n+struct fwu_mdata {\n+\tu32 crc32;\n+\tu32 version;\n+\tu32 active_index;\n+\tu32 previous_active_index;\n+\t/* Followed by image entries or fwu_mdata_ext */\n+} __packed;\n+\n+struct fwu_mdata_ext { /* V2 only */\n+\tu32 metadata_size;\n+\tu16 desc_offset;\n+\tu16 reserved1;\n+\tu8  bank_state[4];\n+\tu32 reserved2;\n+} __packed;\n+\n+/* Metadata access helpers */\n+struct fwu_image_entry *fwu_get_image_entry(struct fwu_mdata *mdata,\n+\t\t\t\t\t    int version, int num_banks,\n+\t\t\t\t\t    int img_id)\n+{\n+\tsize_t offset;\n+\n+\tif (version == 1) {\n+\t\toffset = sizeof(struct fwu_mdata) +\n+\t\t\t(sizeof(struct fwu_image_entry) +\n+\t\t\t sizeof(struct fwu_image_bank_info) * num_banks) * img_id;\n+\t} else {\n+\t\t/* V2: skip fwu_fw_store_desc header */\n+\t\toffset = sizeof(struct fwu_mdata) +\n+\t\t\t sizeof(struct fwu_mdata_ext) +\n+\t\t\t sizeof(struct fwu_fw_store_desc) +\n+\t\t\t (sizeof(struct fwu_image_entry) +\n+\t\t\t  sizeof(struct fwu_image_bank_info) * num_banks) * img_id;\n+\t}\n+\n+\treturn (struct fwu_image_entry *)((char *)mdata + offset);\n+}\n+\n+struct fwu_image_bank_info *fwu_get_bank_info(struct fwu_mdata *mdata,\n+\t\t\t\t\t      int version, int num_banks,\n+\t\t\t\t\t      int img_id, int bank_id)\n+{\n+\tsize_t offset;\n+\n+\tif (version == 1) {\n+\t\toffset = sizeof(struct fwu_mdata) +\n+\t\t\t (sizeof(struct fwu_image_entry) +\n+\t\t\t  sizeof(struct fwu_image_bank_info) * num_banks) * img_id +\n+\t\t\t sizeof(struct fwu_image_entry) +\n+\t\t\t sizeof(struct fwu_image_bank_info) * bank_id;\n+\t} else {\n+\t\toffset = sizeof(struct fwu_mdata) +\n+\t\t\t sizeof(struct fwu_mdata_ext) +\n+\t\t\t sizeof(struct fwu_fw_store_desc) +\n+\t\t\t (sizeof(struct fwu_image_entry) +\n+\t\t\t  sizeof(struct fwu_image_bank_info) * num_banks) * img_id +\n+\t\t\t sizeof(struct fwu_image_entry) +\n+\t\t\t sizeof(struct fwu_image_bank_info) * bank_id;\n+\t}\n+\n+\treturn (struct fwu_image_bank_info *)((char *)mdata + offset);\n+}\n+\n+struct fwu_fw_store_desc *fwu_get_fw_desc(struct fwu_mdata *mdata)\n+{\n+\tsize_t offset;\n+\n+\toffset = sizeof(struct fwu_mdata) +\n+\t\t sizeof(struct fwu_mdata_ext);\n+\n+\treturn (struct fwu_fw_store_desc *)((char *)mdata + offset);\n+}\n+\n+struct fwu_mdata_ext *fwu_get_fw_mdata_ext(struct fwu_mdata *mdata)\n+{\n+\tsize_t offset;\n+\n+\toffset = sizeof(struct fwu_mdata);\n+\n+\treturn (struct fwu_mdata_ext *)((char *)mdata + offset);\n+}\n+\n+#endif /* _FWUMDATA_H_ */\ndiff --git a/tools/fwumdata_src/fwumdata.mk b/tools/fwumdata_src/fwumdata.mk\nindex 00f4ae50dbb..2199e43b372 100644\n--- a/tools/fwumdata_src/fwumdata.mk\n+++ b/tools/fwumdata_src/fwumdata.mk\n@@ -4,4 +4,7 @@\n \n mkfwumdata-objs := fwumdata_src/mkfwumdata.o generated/lib/crc32.o\n HOSTLDLIBS_mkfwumdata += -luuid\n-hostprogs-always-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata\n+hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata\n+\n+fwumdata-objs := fwumdata_src/fwumdata.o generated/lib/crc32.o\n+hostprogs-$(CONFIG_TOOLS_FWUMDATA) += fwumdata\n",
    "prefixes": [
        "v3",
        "5/6"
    ]
}