On Wed, Feb 18, 2026 at 03:17:58PM +0530, Sughosh Ganu wrote: > On Mon, Feb 16, 2026 at 02:35:35PM +0100, Kory Maincent wrote: > > Add a new fwumdata tool to allows users to read, display, and modify FWU > > (Firmware Update) metadata from Linux userspace. It provides functionality > > similar to fw_printenv/fw_setenv but for FWU metadata. Users can view > > metadata, change active/previous bank indices, modify bank states, and set > > image acceptance flags. Configuration is done via fwumdata.config file. > > > > Signed-off-by: Kory Maincent <[email protected]> > > --- > > I am not sure if this has been discussed with Ilias earlier, and > apologies if it has been, but I do think that this patch is not > adhering to the DEN0118 specification, specifically the part mentioned > in section A3.2.1, which says that the metadata is to be maintained by > the Update Agent. I would like to hear from Jose Marinho, who is the > author of the spec, on what he thinks about this approach. > > I do think that the other patches in series look fine, and can be > applied. There is just an issue of inclusion of the tool for > building. I will comment on that patch separately.
Adding Jose to the discussion. -sughosh > > > > MAINTAINERS | 4 + > > doc/develop/uefi/fwu_updates.rst | 4 +- > > doc/fwumdata.1 | 222 ++++++++++ > > tools/.gitignore | 1 + > > tools/fwumdata_src/Kconfig | 11 + > > tools/fwumdata_src/fwumdata.c | 854 > > +++++++++++++++++++++++++++++++++++++ > > tools/fwumdata_src/fwumdata.config | 33 ++ > > tools/fwumdata_src/fwumdata.h | 138 ++++++ > > tools/fwumdata_src/fwumdata.mk | 5 +- > > 9 files changed, 1270 insertions(+), 2 deletions(-) > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 9d954be4a9d..d680b193033 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -1242,11 +1242,15 @@ F: drivers/watchdog/sbsa_gwdt.c > > > > FWU Multi Bank Update > > M: Sughosh Ganu <[email protected]> > > +M: Kory Maincent <[email protected]> > > S: Maintained > > T: git https://source.denx.de/u-boot/custodians/u-boot-efi.git > > +F: doc/fwumdata.1 > > F: doc/mkfwumdata.1 > > F: lib/fwu_updates/* > > F: drivers/fwu-mdata/* > > +F: tools/fwumdata_src/fwumdata.c > > +F: tools/fwumdata_src/fwumdata.h > > F: tools/fwumdata_src/mkfwumdata.c > > > > GATEWORKS_SC > > diff --git a/doc/develop/uefi/fwu_updates.rst > > b/doc/develop/uefi/fwu_updates.rst > > index 84713581459..c592106f8a8 100644 > > --- a/doc/develop/uefi/fwu_updates.rst > > +++ b/doc/develop/uefi/fwu_updates.rst > > @@ -66,7 +66,9 @@ FWU Metadata > > U-Boot supports both versions(1 and 2) of the FWU metadata defined in > > the two revisions of the specification. Support can be enabled for > > either of the two versions through a config flag. The mkfwumdata tool > > -can generate metadata for both the supported versions. > > +can generate metadata for both the supported versions. On the target side, > > +the fwumdata tool can read and update FWU metadata located in memory, > > +similarly to how fw_printenv/fw_setenv works. > > > > Setting up the device for GPT partitioned storage > > ------------------------------------------------- > > diff --git a/doc/fwumdata.1 b/doc/fwumdata.1 > > new file mode 100644 > > index 00000000000..66a53fc9403 > > --- /dev/null > > +++ b/doc/fwumdata.1 > > @@ -0,0 +1,222 @@ > > +.\" SPDX-License-Identifier: GPL-2.0-or-later > > +.\" Copyright (C) 2025 Kory Maincent <[email protected]> > > +.TH FWUMDATA 1 2025 U-Boot > > +.SH NAME > > +fwumdata \- read, display, and modify FWU metadata > > +. > > +.SH SYNOPSIS > > +.SY fwumdata > > +.OP \-c config > > +.OP \-l > > +.OP \-u > > +.OP \-a bankid > > +.OP \-p bankid > > +.RB [ \-s > > +.IR bankid " " state ] > > +.OP \-i imageid > > +.OP \-b bankid > > +.OP \-A > > +.OP \-C > > +.OP \-B num_banks > > +.OP \-I num_images > > +.YS > > +.SY fwumdata > > +.B \-h > > +.YS > > +. > > +.SH DESCRIPTION > > +.B fwumdata > > +reads, displays, and modifies FWU (Firmware Update) metadata from Linux > > +userspace. > > +.PP > > +The tool operates on FWU metadata stored on block or MTD devices, allowing > > +userspace manipulation of firmware update state including active bank > > +selection, image acceptance, and bank state management. > > +. > > +.SH OPTIONS > > +.TP > > +.BR \-c ", " \-\-config " \fIfile\fR" > > +Use custom configuration file. By default, the tool searches for > > +.I ./fwumdata.config > > +then > > +.IR /etc/fwumdata.config . > > +. > > +.TP > > +.BR \-l ", " \-\-list > > +Display detailed metadata information including all GUIDs, image entries, > > +and bank information. Without this option, only a summary is shown. > > +. > > +.TP > > +.BR \-u ", " \-\-update > > +Update metadata if CRC validation fails. Useful for recovering from > > corrupted > > +metadata. > > +. > > +.TP > > +.BR \-a ", " \-\-active " \fIbankid\fR" > > +Set the active bank index to > > +.IR bank . > > +. > > +.TP > > +.BR \-p ", " \-\-previous " \fIbankid\fR" > > +Set the previous active bank index to > > +.IR bank . > > +. > > +.TP > > +.BR \-s ", " \-\-state " \fIbankid state\fR" > > +Set bank index > > +.I bankid > > +to the specified > > +.IR state . > > +Valid states are: > > +.BR accepted , > > +.BR valid , > > +or > > +.BR invalid . > > +Supported only with version 2 metadata. When setting a bank to accepted > > state, > > +all firmware images in that bank are automatically marked as accepted. > > +. > > +.TP > > +.BR \-i ", " \-\-image " \fIimageid\fR" > > +Specify image number (used with > > +.B \-A > > +or > > +.BR \-C ). > > +. > > +.TP > > +.BR \-b ", " \-\-bank " \fIbankid\fR" > > +Specify bank number (used with > > +.B \-A > > +or > > +.BR \-C ). > > +. > > +.TP > > +.BR \-A ", " \-\-accept > > +Accept the image specified by > > +.B \-i > > +in the bank specified by > > +.BR \-b . > > +Sets the FWU_IMAGE_ACCEPTED flag for the image. > > +. > > +.TP > > +.BR \-C ", " \-\-clear > > +Clear the acceptance flag for the image specified by > > +.B \-i > > +in the bank specified by > > +.BR \-b . > > +According to the FWU specification, the bank state is automatically set to > > +invalid before clearing the acceptance flag. > > +. > > +.TP > > +.BR \-B ", " \-\-nbanks " \fInum_banks\fR" > > +Specify total number of banks (required for V1 metadata). > > +. > > +.TP > > +.BR \-I ", " \-\-nimages " \fInum_images\fR" > > +Specify total number of images (required for V1 metadata). > > +. > > +.TP > > +.BR \-h ", " \-\-help > > +Print usage information and exit. > > +. > > +.SH CONFIGURATION FILE > > +The configuration file specifies the location of FWU metadata on storage > > +devices. The format is: > > +.PP > > +.EX > > +.in +4 > > +# Device Name Device Offset Metadata Size Erase Size > > +/dev/mtd0 0x0 0x78 0x1000 > > +/dev/mtd1 0x0 0x78 0x1000 > > +.in > > +.EE > > +.PP > > +Lines starting with > > +.B # > > +are comments. > > +.I Erase Size > > +is optional and only applies to MTD devices; if omitted, it defaults to the > > +metadata size. > > +.PP > > +Specifying two devices enables redundant metadata support. > > +. > > +.SH BUGS > > +Please report bugs to the > > +.UR https://\:source\:.denx\:.de/\:u-boot/\:u-boot/\:issues > > +U-Boot bug tracker > > +.UE . > > +. > > +.SH EXAMPLES > > +Display FWU metadata summary: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata > > +.in > > +.EE > > +.PP > > +Display detailed metadata with all GUIDs: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-l > > +.in > > +.EE > > +.PP > > +Set active bank to 1: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-a 1 > > +.in > > +.EE > > +.PP > > +Set bank 1 to accepted state (automatically accepts all images in that > > bank): > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-s 1 accepted > > +.in > > +.EE > > +.PP > > +Accept image 0 in bank 0: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-i 0 \-b 0 \-A \-l > > +.in > > +.EE > > +.PP > > +Clear acceptance for image 0 in bank 1: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-i 0 \-b 1 \-C \-l > > +.in > > +.EE > > +.PP > > +Clear acceptance for image 1 in bank 1 with metadata V1: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-B 2 \-I 2 \-i 1 \-b 1 \-C \-l > > +.in > > +.EE > > +.PP > > +Use custom configuration file: > > +.PP > > +.EX > > +.in +4 > > +$ \c > > +.B fwumdata \-c /path/to/custom.config > > +.in > > +.EE > > +. > > +.SH SEE ALSO > > +.BR mkfwumdata (1) > > diff --git a/tools/.gitignore b/tools/.gitignore > > index e8daa24a52d..49943d2cf3a 100644 > > --- a/tools/.gitignore > > +++ b/tools/.gitignore > > @@ -11,6 +11,7 @@ > > /file2include > > /fit_check_sign > > /fit_info > > +/fwumdata > > /gdb/gdbcont > > /gdb/gdbsend > > /gen_eth_addr > > diff --git a/tools/fwumdata_src/Kconfig b/tools/fwumdata_src/Kconfig > > index c033c560e8d..af1f3bb3f57 100644 > > --- a/tools/fwumdata_src/Kconfig > > +++ b/tools/fwumdata_src/Kconfig > > @@ -6,3 +6,14 @@ config TOOLS_MKFWUMDATA > > metadata for initial installation of the FWU multi bank > > update on the board. The installation method depends on > > the platform. > > + > > +config TOOLS_FWUMDATA > > + bool "Build fwumdata command" > > + default y if FWU_MULTI_BANK_UPDATE > > + help > > + This command allows users to read, display, and modify FWU > > + (Firmware Update) metadata from Linux userspace. It provides > > + functionality similar to fw_printenv/fw_setenv but for FWU > > + metadata. Users can view metadata, change active/previous > > + bank indices, modify bank states, and set image acceptance > > + flags. Configuration is done via fwumdata.config file. > > diff --git a/tools/fwumdata_src/fwumdata.c b/tools/fwumdata_src/fwumdata.c > > new file mode 100644 > > index 00000000000..c5b0f56842d > > --- /dev/null > > +++ b/tools/fwumdata_src/fwumdata.c > > @@ -0,0 +1,854 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +/* > > + * FWU Metadata Read/Write Tool > > + * Copyright (c) 2025, Kory Maincent <[email protected]> > > + * > > + * Tool to read, display, and modify FWU (Firmware Update) metadata > > + * from Linux userspace. Similar to fw_printenv/fw_setenv for U-Boot > > + * environment, but for FWU metadata. > > + * > > + * Usage: > > + * fwumdata - Print all metadata > > + * fwumdata -u - Print metadata and update it if > > CRC corrupted > > + * fwumdata -c <config> - Use custom config file > > + * fwumdata -a <bank> - Set active bank > > + * fwumdata -p <bank> - Set previous bank > > + * fwumdata -s <bank> <state> - Set bank state (V2 only) > > + * fwumdata -i <id> -b <bank> -A - Accept image > > + * fwumdata -i <id> -b <bank> -C - Clear image acceptance > > + * fwumdata -i <id> -b <bank> > > + * -B <num_banks> > > + * -I <num_images> -C - Clear image acceptance (V1 only) > > + * fwumdata -l - List detailed info with GUIDs > > + */ > > + > > +#include <errno.h> > > +#include <getopt.h> > > +#include <stdio.h> > > +#include <unistd.h> > > +#include <mtd/mtd-user.h> > > +#include <sys/ioctl.h> > > +#include <u-boot/crc.h> > > +#include "fwumdata.h" > > + > > +/* Device configuration */ > > +struct fwumdata_device { > > + const char *devname; > > + long long devoff; > > + unsigned long mdata_size; > > + unsigned long erase_size; > > + int fd; > > + bool is_mtd; > > +}; > > + > > +/* Global state */ > > +static struct fwumdata_device devices[2]; /* Primary and secondary */ > > +static struct fwu_mdata *mdata; > > +static int have_redundant; > > +static struct fwu_mdata *valid_mdata; > > +static bool mdata_mod; > > +static const char *config_file; > > +static int nbanks, nimages; /* For V1 only */ > > +static const char * const default_config_files[] = { > > + "./fwumdata.config", > > + "/etc/fwumdata.config", > > + NULL > > +}; > > + > > +/* GUID/UUID utilities */ > > +static void guid_to_string(const struct efi_guid *guid, char *str) > > +{ > > + sprintf(str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", > > + guid->time_high, guid->time_low, guid->reserved, > > + guid->family, guid->node[0], > > + guid->node[1], guid->node[2], guid->node[3], > > + guid->node[4], guid->node[5], guid->node[6]); > > +} > > + > > +/* Config file parsing */ > > +static int parse_config(const char *fname) > > +{ > > + size_t linesize = 0; > > + char *line = NULL; > > + char *devname; > > + int i = 0; > > + FILE *fp; > > + int rc; > > + > > + fp = fopen(fname, "r"); > > + if (!fp) > > + return -ENOENT; > > + > > + while (i < 2 && getline(&line, &linesize, fp) != -1) { > > + /* Skip comments and empty lines */ > > + if (line[0] == '#' || line[0] == '\n') > > + continue; > > + > > + rc = sscanf(line, "%ms %lli %lx %lx", > > + &devname, > > + &devices[i].devoff, > > + &devices[i].mdata_size, > > + &devices[i].erase_size); > > + > > + if (rc < 3) { > > + free(devname); > > + continue; > > + } > > + > > + if (rc < 4) > > + devices[i].erase_size = devices[i].mdata_size; > > + > > + devices[i].devname = devname; > > + i++; > > + } > > + > > + free(line); > > + fclose(fp); > > + > > + if (i == 2) { > > + have_redundant = true; > > + if (devices[0].mdata_size != devices[1].mdata_size) { > > + fprintf(stderr, > > + "Size mismatch between the two metadata\n"); > > + return -EINVAL; > > + } > > + } > > + > > + if (!i) { > > + fprintf(stderr, > > + "Can't read config %s content\n", fname); > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > +static int find_parse_config(void) > > +{ > > + int i; > > + > > + if (config_file) > > + return parse_config(config_file); > > + > > + for (i = 0; default_config_files[i]; i++) { > > + int ret; > > + > > + ret = parse_config(default_config_files[i]); > > + if (ret == -ENOENT) > > + continue; > > + if (ret) > > + return ret; > > + > > + config_file = default_config_files[i]; > > + return 0; > > + } > > + > > + fprintf(stderr, "Error: Cannot find config file\n"); > > + return -ENOENT; > > +} > > + > > +static int open_device(struct fwumdata_device *dev) > > +{ > > + if (strstr(dev->devname, "/dev/mtd")) > > + dev->is_mtd = true; > > + > > + dev->fd = open(dev->devname, O_RDWR | O_SYNC); > > + if (dev->fd < 0) { > > + fprintf(stderr, "Cannot open %s: %s\n", dev->devname, > > + strerror(errno)); > > + return -ENODEV; > > + } > > + > > + return 0; > > +} > > + > > +static int mtd_erase(int fd, unsigned long offset, unsigned long size) > > +{ > > + struct erase_info_user erase; > > + int ret; > > + > > + erase.start = offset; > > + erase.length = size; > > + > > + ret = ioctl(fd, MEMERASE, &erase); > > + if (ret < 0) { > > + fprintf(stderr, "MTD erase failed: %s\n", strerror(errno)); > > + return -errno; > > + } > > + > > + return 0; > > +} > > + > > +static int read_device(struct fwumdata_device *dev, void *buf, size_t > > count) > > +{ > > + if (lseek(dev->fd, dev->devoff, SEEK_SET) < 0) { > > + fprintf(stderr, "Seek failed: %s\n", strerror(errno)); > > + return -errno; > > + } > > + > > + if (read(dev->fd, buf, count) < 0) { > > + fprintf(stderr, "Read failed: %s\n", strerror(errno)); > > + return -errno; > > + } > > + > > + return 0; > > +} > > + > > +static int write_device(struct fwumdata_device *dev, const void *buf, > > + size_t count) > > +{ > > + int ret; > > + > > + /* Erase if MTD device */ > > + if (dev->is_mtd) { > > + ret = mtd_erase(dev->fd, dev->devoff, dev->erase_size); > > + if (ret) > > + return ret; > > + } > > + > > + if (lseek(dev->fd, dev->devoff, SEEK_SET) < 0) { > > + fprintf(stderr, "Seek failed: %s\n", strerror(errno)); > > + return -errno; > > + } > > + > > + if (write(dev->fd, buf, count) < 0) { > > + fprintf(stderr, "Write failed: %s\n", strerror(errno)); > > + return -errno; > > + } > > + > > + return 0; > > +} > > + > > +/* Metadata operations */ > > +static int validate_crc(struct fwu_mdata *mdata, size_t size) > > +{ > > + u32 calc_crc, stored_crc; > > + > > + stored_crc = mdata->crc32; > > + calc_crc = crc32(0, (const u8 *)&mdata->version, size - sizeof(u32)); > > + > > + if (calc_crc != stored_crc) { > > + fprintf(stderr, > > + "CRC mismatch: calculated 0x%08x, stored 0x%08x\n", > > + calc_crc, stored_crc); > > + if (mdata->version == 1) > > + fprintf(stderr, > > + "Metadata is V1, this may be size description > > issue\n"); > > + return -1; > > + } > > + > > + return 0; > > +} > > + > > +static void update_crc(struct fwu_mdata *mdata, size_t size) > > +{ > > + mdata->crc32 = crc32(0, (const u8 *)&mdata->version, size - > > sizeof(u32)); > > +} > > + > > +static int read_one_metadata(int mdata_id, size_t size) > > +{ > > + int ret; > > + > > + ret = open_device(&devices[mdata_id]); > > + if (ret) > > + return ret; > > + > > + ret = read_device(&devices[mdata_id], &mdata[mdata_id], size); > > + if (ret) > > + return ret; > > + > > + if (mdata[mdata_id].version != 1 && mdata[mdata_id].version != 2) { > > + fprintf(stderr, "Invalid metadata %d version: %u\n", > > + mdata_id, mdata[mdata_id].version); > > + } > > + > > + return 0; > > +} > > + > > +static int read_metadata(bool update) > > +{ > > + size_t alloc_size; > > + int ret; > > + > > + /* Allocate initial buffer */ > > + alloc_size = devices[0].mdata_size; > > + mdata = calloc(have_redundant ? 2 : 1, alloc_size); > > + if (!mdata) { > > + fprintf(stderr, "Memory allocation failed\n"); > > + return -ENOMEM; > > + } > > + > > + ret = read_one_metadata(0, alloc_size); > > + if (ret) > > + return ret; > > + > > + if (validate_crc(&mdata[0], alloc_size) < 0) { > > + fprintf(stderr, > > + "Warning: Primary metadata CRC validation failed\n"); > > + mdata_mod = update; > > + } else { > > + valid_mdata = &mdata[0]; > > + } > > + > > + if (have_redundant) { > > + ret = read_one_metadata(1, alloc_size); > > + if (ret) > > + return ret; > > + > > + if (validate_crc(&mdata[1], alloc_size) < 0) { > > + fprintf(stderr, > > + "Warning: Secondary metadata CRC validation > > failed\n"); > > + mdata_mod = update; > > + } else if (valid_mdata && mdata[0].crc32 != mdata[1].crc32) { > > + fprintf(stderr, > > + "Metadatas valid but not equal, use first one > > as default\n"); > > + mdata_mod = update; > > + } else { > > + valid_mdata = &mdata[1]; > > + } > > + } > > + > > + if (!valid_mdata) { > > + fprintf(stderr, > > + "No metadata valid, use first one as default\n"); > > + mdata_mod = update; > > + valid_mdata = &mdata[0]; > > + } > > + > > + if (valid_mdata->version == 2) { > > + struct fwu_mdata_ext *mdata_ext; > > + > > + mdata_ext = fwu_get_fw_mdata_ext(valid_mdata); > > + if (mdata_ext->metadata_size != alloc_size) { > > + fprintf(stderr, > > + "Metadata real size 0x%x mismatch with the > > config 0x%zx\n", > > + mdata_ext->metadata_size, alloc_size); > > + return -EINVAL; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int write_metadata(void) > > +{ > > + size_t write_size = devices[0].mdata_size; > > + int ret; > > + > > + if (!mdata_mod) > > + return 0; > > + > > + /* Update CRC */ > > + update_crc(valid_mdata, write_size); > > + > > + /* Write primary */ > > + ret = write_device(&devices[0], valid_mdata, write_size); > > + if (ret < 0) { > > + fprintf(stderr, "Failed to write primary metadata\n"); > > + return ret; > > + } > > + > > + /* Write secondary if redundant */ > > + if (have_redundant) { > > + ret = write_device(&devices[1], valid_mdata, write_size); > > + if (ret < 0) { > > + fprintf(stderr, "Failed to write secondary metadata\n"); > > + return -1; > > + } > > + } > > + > > + printf("FWU metadata updated successfully\n"); > > + mdata_mod = 0; > > + > > + return 0; > > +} > > + > > +/* Display functions */ > > +static const char *bank_state_to_string(u8 state) > > +{ > > + switch (state) { > > + case FWU_BANK_ACCEPTED: > > + return "accepted"; > > + case FWU_BANK_VALID: > > + return "valid"; > > + case FWU_BANK_INVALID: > > + return "invalid"; > > + default: > > + return "unknown"; > > + } > > +} > > + > > +static void print_metadata_summary(void) > > +{ > > + int i; > > + > > + printf("FWU Metadata:\n"); > > + printf("\tVersion: %u\n", valid_mdata->version); > > + printf("\tActive Index: %u\n", valid_mdata->active_index); > > + printf("\tPrevious Index: %u\n", > > valid_mdata->previous_active_index); > > + printf("\tCRC32: 0x%08x\n", valid_mdata->crc32); > > + > > + if (valid_mdata->version == 2) { > > + struct fwu_fw_store_desc *fw_desc; > > + struct fwu_mdata_ext *mdata_ext; > > + > > + mdata_ext = fwu_get_fw_mdata_ext(valid_mdata); > > + printf("\tMetadata Size: %u bytes\n", > > mdata_ext->metadata_size); > > + printf("\tDescriptor Offset: %u\n", mdata_ext->desc_offset); > > + printf("\tBank States:\n"); > > + > > + fw_desc = fwu_get_fw_desc(valid_mdata); > > + for (i = 0; i < fw_desc->num_banks && i < MAX_BANKS_V2; i++) { > > + printf("\t\tBank %d: %s (0x%02x)\n", i, > > + bank_state_to_string(mdata_ext->bank_state[i]), > > + mdata_ext->bank_state[i]); > > + } > > + } > > +} > > + > > +static void print_metadata_detailed(void) > > +{ > > + struct fwu_fw_store_desc *fw_desc = NULL; > > + struct fwu_image_bank_info *bank_info; > > + struct fwu_image_entry *img_entry; > > + int num_images, num_banks; > > + char guid_str[64]; > > + int i, j; > > + > > + print_metadata_summary(); > > + > > + if (valid_mdata->version == 1) { > > + num_images = nimages; > > + num_banks = nbanks; > > + } else { > > + fw_desc = fwu_get_fw_desc(valid_mdata); > > + num_images = fw_desc->num_images; > > + num_banks = fw_desc->num_banks; > > + } > > + > > + if (fw_desc) { > > + printf("\n\tFirmware Store Descriptor:\n"); > > + printf("\t\tNumber of Banks: %u\n", num_banks); > > + printf("\t\tNumber of Images: %u\n", num_images); > > + printf("\t\tImage Entry Size: %u\n", > > fw_desc->img_entry_size); > > + printf("\t\tBank Info Entry Size: %u\n", > > fw_desc->bank_info_entry_size); > > + } > > + > > + printf("\n\tImages:\n"); > > + for (i = 0; i < num_images; i++) { > > + img_entry = fwu_get_image_entry(valid_mdata, > > valid_mdata->version, > > + num_banks, i); > > + > > + printf("\t\tImage %d:\n", i); > > + > > + guid_to_string(&img_entry->image_type_guid, guid_str); > > + printf("\t\t\tImage Type GUID: %s\n", guid_str); > > + > > + guid_to_string(&img_entry->location_guid, guid_str); > > + printf("\t\t\tLocation GUID: %s\n", guid_str); > > + > > + printf("\t\t\tBanks:\n"); > > + for (j = 0; j < num_banks; j++) { > > + bank_info = fwu_get_bank_info(valid_mdata, > > + valid_mdata->version, > > + num_banks, i, j); > > + > > + guid_to_string(&bank_info->image_guid, guid_str); > > + printf("\t\t\t\tBank %d:\n", j); > > + printf("\t\t\t\t\tImage GUID: %s\n", guid_str); > > + printf("\t\t\t\t\tAccepted: %s (%u)\n", > > + (bank_info->accepted & FWU_IMAGE_ACCEPTED) ? > > "yes" : "no", > > + bank_info->accepted); > > + } > > + } > > +} > > + > > +/* Modification functions */ > > +static int set_active_index(int bank) > > +{ > > + struct fwu_fw_store_desc *fw_desc; > > + int num_banks; > > + > > + if (valid_mdata->version == 2) { > > + fw_desc = fwu_get_fw_desc(valid_mdata); > > + num_banks = fw_desc->num_banks; > > + } else { > > + num_banks = nbanks; > > + } > > + > > + if (bank < 0 || bank >= num_banks) { > > + fprintf(stderr, "Error: Invalid bank %d (must be 0-%d)\n", > > + bank, num_banks - 1); > > + return -EINVAL; > > + } > > + > > + if (valid_mdata->active_index == bank) > > + return 0; > > + > > + valid_mdata->active_index = bank; > > + mdata_mod = 1; > > + > > + printf("Active bank set to %d\n", bank); > > + return 0; > > +} > > + > > +static int set_previous_index(int bank) > > +{ > > + struct fwu_fw_store_desc *fw_desc; > > + int num_banks; > > + > > + if (valid_mdata->version == 2) { > > + fw_desc = fwu_get_fw_desc(valid_mdata); > > + num_banks = fw_desc->num_banks; > > + } else { > > + num_banks = nbanks; > > + } > > + > > + if (bank < 0 || bank >= num_banks) { > > + fprintf(stderr, "Error: Invalid bank %d (must be 0-%d)\n", > > + bank, num_banks - 1); > > + return -EINVAL; > > + } > > + > > + if (valid_mdata->previous_active_index == bank) > > + return 0; > > + > > + valid_mdata->previous_active_index = bank; > > + mdata_mod = 1; > > + > > + printf("Previous bank set to %d\n", bank); > > + return 0; > > +} > > + > > +static int set_image_accepted(int image, int bank, int accept) > > +{ > > + struct fwu_image_bank_info *bank_info; > > + int num_images, num_banks; > > + > > + if (valid_mdata->version == 1) { > > + num_images = nimages; > > + num_banks = nbanks; > > + } else { > > + struct fwu_fw_store_desc *fw_desc; > > + > > + fw_desc = fwu_get_fw_desc(valid_mdata); > > + num_images = fw_desc->num_images; > > + num_banks = fw_desc->num_banks; > > + } > > + > > + if (bank < 0 || bank >= num_banks) { > > + fprintf(stderr, "Error: Invalid bank %d (must be 0-%d)\n", > > + bank, num_banks - 1); > > + return -EINVAL; > > + } > > + > > + if (image < 0 || image >= num_images) { > > + fprintf(stderr, "Error: Invalid image %d (must be 0-%d)\n", > > + image, num_images - 1); > > + return -EINVAL; > > + } > > + > > + bank_info = fwu_get_bank_info(valid_mdata, valid_mdata->version, > > + num_banks, image, bank); > > + if (accept == bank_info->accepted) > > + return 0; > > + > > + if (accept) { > > + bank_info->accepted = FWU_IMAGE_ACCEPTED; > > + } else { > > + bank_info->accepted = 0; > > + > > + /* According to the spec: bank_state[index] have to be set > > + * to invalid before any content in the img_bank_info[index] > > + * is overwritten. > > + */ > > + if (valid_mdata->version == 2) { > > + struct fwu_mdata_ext *mdata_ext; > > + > > + mdata_ext = fwu_get_fw_mdata_ext(valid_mdata); > > + mdata_ext->bank_state[bank] = FWU_BANK_INVALID; > > + } > > + } > > + > > + mdata_mod = 1; > > + printf("Image %d in bank %d: acceptance %s\n", > > + image, bank, accept ? "set" : "cleared"); > > + > > + return 0; > > +} > > + > > +static int set_bank_state(int bank, const char *state_str) > > +{ > > + struct fwu_fw_store_desc *fw_desc; > > + struct fwu_mdata_ext *mdata_ext; > > + u8 state; > > + int i; > > + > > + if (valid_mdata->version != 2) { > > + fprintf(stderr, > > + "Error: Bank state is only supported in V2 metadata\n"); > > + return -EINVAL; > > + } > > + > > + fw_desc = fwu_get_fw_desc(valid_mdata); > > + mdata_ext = fwu_get_fw_mdata_ext(valid_mdata); > > + > > + if (bank < 0 || bank >= fw_desc->num_banks || bank >= MAX_BANKS_V2) { > > + fprintf(stderr, "Error: Invalid bank %d (must be 0-%d)\n", > > + bank, fw_desc->num_banks - 1); > > + return -EINVAL; > > + } > > + > > + /* Parse state string */ > > + if (!strcmp(state_str, "accepted")) { > > + state = FWU_BANK_ACCEPTED; > > + } else if (!strcmp(state_str, "valid")) { > > + state = FWU_BANK_VALID; > > + } else if (!strcmp(state_str, "invalid")) { > > + state = FWU_BANK_INVALID; > > + } else { > > + fprintf(stderr, > > + "Error: Invalid state '%s' (must be > > accepted/valid/invalid)\n", > > + state_str); > > + return -EINVAL; > > + } > > + > > + if (mdata_ext->bank_state[bank] == state) > > + return 0; > > + > > + /* If a bank is set in a accepted state all firmware images in > > + * that bank must be marked as accepted as described in the spec. > > + */ > > + if (state == FWU_BANK_ACCEPTED) { > > + for (i = 0; i < fw_desc->num_images; i++) { > > + int ret; > > + > > + ret = set_image_accepted(i, bank, true); > > + if (ret) > > + return ret; > > + } > > + } > > + mdata_ext->bank_state[bank] = state; > > + mdata_mod = 1; > > + > > + printf("Bank %d state set to %s (0x%02x)\n", bank, state_str, state); > > + return 0; > > +} > > + > > +static int metadata_v1_validate_size(void) > > +{ > > + int calc_size; > > + > > + calc_size = sizeof(struct fwu_mdata) + > > + (sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * nbanks) * nimages; > > + > > + if (devices[0].mdata_size != calc_size) { > > + fprintf(stderr, > > + "Metadata calculate size (-B and -I options) 0x%x > > mismatch with the config 0x%zx\n", > > + calc_size, devices[0].mdata_size); > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > +/* Command-line interface */ > > +static void print_usage(void) > > +{ > > + fprintf(stderr, "Usage: fwumdata [options]\n\n"); > > + fprintf(stderr, "Options:\n" > > + "\t-c, --config <file> Use custom config file, > > defaults:\n" > > + "\t ./fwumdata.config or > > /etc/fwumdata.config\n" > > + "\t-l, --list List detailed metadata with > > GUIDs\n" > > + "\t-a, --active <bank> Set active bank index\n" > > + "\t-p, --previous <bank> Set previous bank index\n" > > + "\t-s, --state <bank> <state> Set bank state (V2 only)\n" > > + "\t state: > > accepted|valid|invalid\n" > > + "\t-i, --image <id> Image number (for -A/-C)\n" > > + "\t-b, --bank <bank> Bank number (for -A/-C)\n" > > + "\t-A, --accept Accept image (requires -i and > > -b)\n" > > + "\t-C, --clear Clear image acceptance (requires > > -i and -b)\n" > > + "\t-u, --update Update metadata if there is a > > checksum issue\n" > > + "\t-B, --nbanks <num_banks> Number of banks (required for V1 > > metadata)\n" > > + "\t-I, --nimages <num_images> Number of images (required for > > V1 metadata)\n" > > + "\t-h, --help Print this help\n\n"); > > + fprintf(stderr, "Config file format (fwumdata.config):\n" > > + "\t# Device Name Device Offset Metadata Size Erase > > Size\n" > > + "\t/dev/mtd0 0x0 0x78 > > 0x1000\n" > > + "\t/dev/mtd1 0x0 0x78 > > 0x1000\n\n"); > > + fprintf(stderr, "Examples:\n" > > + "\tfwumdata # Print metadata > > summary\n" > > + "\tfwumdata -l # Print detailed > > metadata\n" > > + "\tfwumdata -a 1 # Set active bank to > > 1\n" > > + "\tfwumdata -s 1 accepted # Set bank 1 to > > accepted state\n" > > + "\tfwumdata -i 0 -b 0 -A # Accept image in bank > > 0\n" > > + "\tfwumdata -B 2 -I 2 -i 1 -b 1 -A -l # Accept image 1 in > > bank 1 with metadata V1\n"); > > +} > > + > > +int main(int argc, char *argv[]) > > +{ > > + char *bank_state_str = NULL; > > + bool list_detailed = false; > > + int bank_state_num = -1; > > + int active_index = -1; > > + int bank_id = -1; > > + int prev_index = -1; > > + bool do_accept = 0; > > + bool do_clear = 0; > > + bool do_update = 0; > > + int image_id = -1; > > + int ret = 0; > > + int opt; > > + > > + static struct option long_options[] = { > > + {"config", required_argument, 0, 'c'}, > > + {"list", no_argument, 0, 'l'}, > > + {"active", required_argument, 0, 'a'}, > > + {"previous", required_argument, 0, 'p'}, > > + {"state", required_argument, 0, 's'}, > > + {"image", required_argument, 0, 'i'}, > > + {"bank", required_argument, 0, 'b'}, > > + {"accept", no_argument, 0, 'A'}, > > + {"clear", no_argument, 0, 'C'}, > > + {"update", no_argument, 0, 'u'}, > > + {"nbanks", required_argument, 0, 'B'}, > > + {"nimages", required_argument, 0, 'I'}, > > + {"help", no_argument, 0, 'h'}, > > + {0, 0, 0, 0} > > + }; > > + > > + /* Parse arguments */ > > + while ((opt = getopt_long(argc, argv, "c:la:p:s:i:b:ACuB:I:h", > > long_options, NULL)) != -1) { > > + switch (opt) { > > + case 'c': > > + config_file = optarg; > > + break; > > + case 'l': > > + list_detailed = 1; > > + break; > > + case 'a': > > + active_index = atoi(optarg); > > + break; > > + case 'p': > > + prev_index = atoi(optarg); > > + break; > > + case 's': > > + bank_state_num = atoi(optarg); > > + if (optind < argc && argv[optind][0] != '-') { > > + bank_state_str = argv[optind++]; > > + } else { > > + fprintf(stderr, > > + "Error: -s requires bank number and > > state\n"); > > + return 1; > > + } > > + break; > > + case 'i': > > + image_id = atoi(optarg); > > + break; > > + case 'b': > > + bank_id = atoi(optarg); > > + break; > > + case 'A': > > + do_accept = 1; > > + break; > > + case 'C': > > + do_clear = 1; > > + break; > > + case 'u': > > + do_update = 1; > > + break; > > + case 'B': > > + nbanks = atoi(optarg); > > + break; > > + case 'I': > > + nimages = atoi(optarg); > > + break; > > + case 'h': > > + print_usage(); > > + return 0; > > + default: > > + print_usage(); > > + return 1; > > + } > > + } > > + > > + ret = find_parse_config(); > > + if (ret < 0) { > > + fprintf(stderr, "Error: Cannot read configuration\n"); > > + return ret; > > + } > > + > > + ret = read_metadata(do_update); > > + if (ret < 0) { > > + fprintf(stderr, "Error: Cannot read metadata\n"); > > + goto cleanup; > > + } > > + > > + if (valid_mdata->version == 1) { > > + ret = metadata_v1_validate_size(); > > + if (ret) > > + goto cleanup; > > + } > > + > > + /* Perform operations */ > > + if (active_index >= 0) { > > + ret = set_active_index(active_index); > > + if (ret < 0) > > + goto cleanup; > > + } > > + > > + if (prev_index >= 0) { > > + ret = set_previous_index(prev_index); > > + if (ret < 0) > > + goto cleanup; > > + } > > + > > + if (do_accept || do_clear) { > > + if (image_id < 0 || bank_id < 0) { > > + fprintf(stderr, > > + "Error: -A/-C requires both -i <guid> and -b > > <bank>\n"); > > + ret = -EINVAL; > > + goto cleanup; > > + } > > + > > + ret = set_image_accepted(image_id, bank_id, do_accept); > > + if (ret < 0) > > + goto cleanup; > > + } > > + > > + if (bank_state_num >= 0 && bank_state_str) { > > + ret = set_bank_state(bank_state_num, bank_state_str); > > + if (ret < 0) > > + goto cleanup; > > + } > > + > > + /* Write back if modified */ > > + if (mdata_mod) { > > + ret = write_metadata(); > > + if (ret) > > + goto cleanup; > > + } > > + > > + /* Display metadata if no modifications or list requested */ > > + if (list_detailed) > > + print_metadata_detailed(); > > + else > > + print_metadata_summary(); > > + > > +cleanup: > > + /* Close devices and free memory */ > > + if (devices[0].fd) > > + close(devices[0].fd); > > + if (devices[1].fd) > > + close(devices[1].fd); > > + > > + free(mdata); > > + > > + for (int i = 0; i < 2; i++) { > > + if (devices[i].devname) > > + free((void *)devices[i].devname); > > + } > > + > > + return ret; > > +} > > diff --git a/tools/fwumdata_src/fwumdata.config > > b/tools/fwumdata_src/fwumdata.config > > new file mode 100644 > > index 00000000000..7e83f7a5909 > > --- /dev/null > > +++ b/tools/fwumdata_src/fwumdata.config > > @@ -0,0 +1,33 @@ > > +# FWU Metadata Configuration File > > +# > > +# Format: <device> <offset> <metadata_size> <erase_size> > > +# > > +# This file describes where the FWU metadata is stored. You can specify > > +# up to two entries for redundant metadata copies. > > +# > > +# Device: MTD device (/dev/mtdX), block device (/dev/mmcblkX), or file path > > +# Offset: Byte offset from start of device (hex with 0x prefix) > > +# Metadata Size: Size of metadata structure in bytes (hex with 0x prefix) > > +# Erase Size: Sector/erase block size (hex with 0x prefix, defaults to > > +# metadata_size, required only for MTD device) > > +# > > +# Examples: > > +# > > +# MTD devices (NOR/NAND flash): > > +# /dev/mtd0 0x0 0x1000 0x1000 > > +# /dev/mtd1 0x0 0x1000 0x1000 > > +# > > +# Block device (eMMC/SD): > > +# /dev/mmcblk0 0x100000 0x78 > > +# /dev/mmcblk0 0x101000 0x78 > > +# > > +# or: > > +# /dev/disk/by-partlabel/metadata1 0 0x78 > > +# /dev/disk/by-partlabel/metadata2 0 0x78 > > +# > > +# Regular file: > > +# /boot/fwu-mdata.bin 0x0 0x78 > > +# > > +# Default configuration (update for your platform): > > +/dev/mtd0 0x0 0x78 0x1000 > > +/dev/mtd1 0x0 0x78 0x1000 > > diff --git a/tools/fwumdata_src/fwumdata.h b/tools/fwumdata_src/fwumdata.h > > new file mode 100644 > > index 00000000000..5e2c45d0fb0 > > --- /dev/null > > +++ b/tools/fwumdata_src/fwumdata.h > > @@ -0,0 +1,138 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (c) 2025, Kory Maincent <[email protected]> > > + */ > > + > > +#ifndef _FWUMDATA_H_ > > +#define _FWUMDATA_H_ > > + > > +#include <linux/compiler_attributes.h> > > + > > +/* Type definitions for U-Boot compatibility */ > > +typedef uint8_t u8; > > +typedef uint16_t u16; > > +typedef uint32_t u32; > > +typedef uint64_t u64; > > + > > +/* FWU Constants */ > > +#define FWU_IMAGE_ACCEPTED 0x1 > > +#define FWU_BANK_INVALID (uint8_t)0xFF > > +#define FWU_BANK_VALID (uint8_t)0xFE > > +#define FWU_BANK_ACCEPTED (uint8_t)0xFC > > +#define MAX_BANKS_V2 4 > > + > > +/* EFI GUID structure */ > > +struct efi_guid { > > + u32 time_high; > > + u16 time_low; > > + u16 reserved; > > + u8 family; > > + u8 node[7]; > > +} __packed; > > + > > +/* FWU Metadata structures */ > > +struct fwu_image_bank_info { > > + struct efi_guid image_guid; > > + u32 accepted; > > + u32 reserved; > > +} __packed; > > + > > +struct fwu_image_entry { > > + struct efi_guid image_type_guid; > > + struct efi_guid location_guid; > > + struct fwu_image_bank_info img_bank_info[0]; /* Variable length */ > > +} __packed; > > + > > +struct fwu_fw_store_desc { > > + u8 num_banks; > > + u8 reserved; > > + u16 num_images; > > + u16 img_entry_size; > > + u16 bank_info_entry_size; > > + struct fwu_image_entry img_entry[0]; /* Variable length */ > > +} __packed; > > + > > +struct fwu_mdata { > > + u32 crc32; > > + u32 version; > > + u32 active_index; > > + u32 previous_active_index; > > + /* Followed by image entries or fwu_mdata_ext */ > > +} __packed; > > + > > +struct fwu_mdata_ext { /* V2 only */ > > + u32 metadata_size; > > + u16 desc_offset; > > + u16 reserved1; > > + u8 bank_state[4]; > > + u32 reserved2; > > +} __packed; > > + > > +/* Metadata access helpers */ > > +struct fwu_image_entry *fwu_get_image_entry(struct fwu_mdata *mdata, > > + int version, int num_banks, > > + int img_id) > > +{ > > + size_t offset; > > + > > + if (version == 1) { > > + offset = sizeof(struct fwu_mdata) + > > + (sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * num_banks) * > > img_id; > > + } else { > > + /* V2: skip fwu_fw_store_desc header */ > > + offset = sizeof(struct fwu_mdata) + > > + sizeof(struct fwu_mdata_ext) + > > + sizeof(struct fwu_fw_store_desc) + > > + (sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * num_banks) * > > img_id; > > + } > > + > > + return (struct fwu_image_entry *)((char *)mdata + offset); > > +} > > + > > +struct fwu_image_bank_info *fwu_get_bank_info(struct fwu_mdata *mdata, > > + int version, int num_banks, > > + int img_id, int bank_id) > > +{ > > + size_t offset; > > + > > + if (version == 1) { > > + offset = sizeof(struct fwu_mdata) + > > + (sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * num_banks) * > > img_id + > > + sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * bank_id; > > + } else { > > + offset = sizeof(struct fwu_mdata) + > > + sizeof(struct fwu_mdata_ext) + > > + sizeof(struct fwu_fw_store_desc) + > > + (sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * num_banks) * > > img_id + > > + sizeof(struct fwu_image_entry) + > > + sizeof(struct fwu_image_bank_info) * bank_id; > > + } > > + > > + return (struct fwu_image_bank_info *)((char *)mdata + offset); > > +} > > + > > +struct fwu_fw_store_desc *fwu_get_fw_desc(struct fwu_mdata *mdata) > > +{ > > + size_t offset; > > + > > + offset = sizeof(struct fwu_mdata) + > > + sizeof(struct fwu_mdata_ext); > > + > > + return (struct fwu_fw_store_desc *)((char *)mdata + offset); > > +} > > + > > +struct fwu_mdata_ext *fwu_get_fw_mdata_ext(struct fwu_mdata *mdata) > > +{ > > + size_t offset; > > + > > + offset = sizeof(struct fwu_mdata); > > + > > + return (struct fwu_mdata_ext *)((char *)mdata + offset); > > +} > > + > > +#endif /* _FWUMDATA_H_ */ > > diff --git a/tools/fwumdata_src/fwumdata.mk b/tools/fwumdata_src/fwumdata.mk > > index 00f4ae50dbb..2199e43b372 100644 > > --- a/tools/fwumdata_src/fwumdata.mk > > +++ b/tools/fwumdata_src/fwumdata.mk > > @@ -4,4 +4,7 @@ > > > > mkfwumdata-objs := fwumdata_src/mkfwumdata.o generated/lib/crc32.o > > HOSTLDLIBS_mkfwumdata += -luuid > > -hostprogs-always-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata > > +hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata > > + > > +fwumdata-objs := fwumdata_src/fwumdata.o generated/lib/crc32.o > > +hostprogs-$(CONFIG_TOOLS_FWUMDATA) += fwumdata > > > > -- > > 2.43.0 > > > > -sughosh -sughosh

