From 1c821b592f14d3ec5704829de112da952388a654 Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 23 Feb 2026 14:18:35 +0100 Subject: tools: gitignore: Add mkfwumdata to the git ignore file mkfwumdata is a built image. Add it to .gitignore. Signed-off-by: Kory Maincent Tested-by: Dario Binacchi Signed-off-by: Ilias Apalodimas --- tools/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/.gitignore b/tools/.gitignore index 6a5c613f772..e8daa24a52d 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -24,6 +24,7 @@ /mkeficapsule /mkenvimage /mkexynosspl +/mkfwumdata /mkimage /mksunxiboot /mxsboot -- cgit v1.2.3 From 876fc8df120bfdc40648e904662a7c6fbf1e543e Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 23 Feb 2026 14:18:36 +0100 Subject: tools: Reorganize mkfwumdata tool into fwumdata_src directory Update FWU metadata-related tools by moving mkfwumdata.c into a new tools/fwumdata_src/ directory structure. This refactoring prepares for the addition of the fwumdata runtime tool, which will allow editing FWU metadata directly from the target. The Kconfig and Makefile entries are also moved into separate files within the new directory (Kconfig and fwumdata.mk respectively) to keep all FWU metadata tool configurations together and improve maintainability. Signed-off-by: Kory Maincent Tested-by: Dario Binacchi Signed-off-by: Ilias Apalodimas --- MAINTAINERS | 3 +- tools/Kconfig | 9 +- tools/Makefile | 4 +- tools/fwumdata_src/Kconfig | 8 + tools/fwumdata_src/fwumdata.mk | 7 + tools/fwumdata_src/mkfwumdata.c | 493 ++++++++++++++++++++++++++++++++++++++++ tools/mkfwumdata.c | 493 ---------------------------------------- 7 files changed, 512 insertions(+), 505 deletions(-) create mode 100644 tools/fwumdata_src/Kconfig create mode 100644 tools/fwumdata_src/fwumdata.mk create mode 100644 tools/fwumdata_src/mkfwumdata.c delete mode 100644 tools/mkfwumdata.c diff --git a/MAINTAINERS b/MAINTAINERS index 061717c8fe5..a925247b0cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1246,9 +1246,10 @@ FWU Multi Bank Update M: Sughosh Ganu S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-efi.git +F: doc/mkfwumdata.1 F: lib/fwu_updates/* F: drivers/fwu-mdata/* -F: tools/mkfwumdata.c +F: tools/fwumdata_src/mkfwumdata.c GATEWORKS_SC M: Tim Harvey diff --git a/tools/Kconfig b/tools/Kconfig index a1b4d701ac7..ef33295b8ec 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -194,13 +194,6 @@ config LUT_SEQUENCE help Look Up Table Sequence -config TOOLS_MKFWUMDATA - bool "Build mkfwumdata command" - default y if FWU_MULTI_BANK_UPDATE - help - This command allows users to create a raw image of the FWU - metadata for initial installation of the FWU multi bank - update on the board. The installation method depends on - the platform. +source tools/fwumdata_src/Kconfig endmenu diff --git a/tools/Makefile b/tools/Makefile index 0ac683ac3ec..1908a63b42c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -272,9 +272,7 @@ mkeficapsule-objs := generated/lib/uuid.o \ mkeficapsule.o hostprogs-always-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule -mkfwumdata-objs := mkfwumdata.o generated/lib/crc32.o -HOSTLDLIBS_mkfwumdata += -luuid -hostprogs-always-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata +include tools/fwumdata_src/fwumdata.mk # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of diff --git a/tools/fwumdata_src/Kconfig b/tools/fwumdata_src/Kconfig new file mode 100644 index 00000000000..c033c560e8d --- /dev/null +++ b/tools/fwumdata_src/Kconfig @@ -0,0 +1,8 @@ +config TOOLS_MKFWUMDATA + bool "Build mkfwumdata command" + default y if FWU_MULTI_BANK_UPDATE + help + This command allows users to create a raw image of the FWU + metadata for initial installation of the FWU multi bank + update on the board. The installation method depends on + the platform. diff --git a/tools/fwumdata_src/fwumdata.mk b/tools/fwumdata_src/fwumdata.mk new file mode 100644 index 00000000000..00f4ae50dbb --- /dev/null +++ b/tools/fwumdata_src/fwumdata.mk @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2025, Kory Maincent + +mkfwumdata-objs := fwumdata_src/mkfwumdata.o generated/lib/crc32.o +HOSTLDLIBS_mkfwumdata += -luuid +hostprogs-always-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata diff --git a/tools/fwumdata_src/mkfwumdata.c b/tools/fwumdata_src/mkfwumdata.c new file mode 100644 index 00000000000..fbc2067bc12 --- /dev/null +++ b/tools/fwumdata_src/mkfwumdata.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#undef CONFIG_FWU_NUM_BANKS +#undef CONFIG_FWU_NUM_IMAGES_PER_BANK + +/* This will dynamically allocate the fwu_mdata */ +#define CONFIG_FWU_NUM_BANKS 0 +#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0 + +/* version 2 supports maximum of 4 banks */ +#define MAX_BANKS_V2 4 + +#define BANK_INVALID (u8)0xFF +#define BANK_ACCEPTED (u8)0xFC + +#include + +static const char *opts_short = "b:i:a:p:v:V:gh"; + +static struct option options[] = { + {"banks", required_argument, NULL, 'b'}, + {"images", required_argument, NULL, 'i'}, + {"guid", required_argument, NULL, 'g'}, + {"active-bank", required_argument, NULL, 'a'}, + {"previous-bank", required_argument, NULL, 'p'}, + {"version", required_argument, NULL, 'v'}, + {"vendor-file", required_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}, +}; + +static void print_usage(void) +{ + fprintf(stderr, "Usage: mkfwumdata [options] \n"); + fprintf(stderr, "Options:\n" + "\t-i, --images Number of images (mandatory)\n" + "\t-b, --banks Number of banks (mandatory)\n" + "\t-v, --version Metadata version (mandatory)\n" + "\t-a, --active-bank Active bank (default=0)\n" + "\t-p, --previous-bank Previous active bank (default=active_bank - 1)\n" + "\t-g, --guid Use GUID instead of UUID\n" + "\t-V, --vendor-file Vendor data file to append to the metadata\n" + "\t-h, --help print a help message\n" + ); + fprintf(stderr, " UUIDs list syntax:\n" + "\t ,,\n" + "\t images uuid list syntax:\n" + "\t img_uuid_00,img_uuid_01...img_uuid_0b,\n" + "\t img_uuid_10,img_uuid_11...img_uuid_1b,\n" + "\t ...,\n" + "\t img_uuid_i0,img_uuid_i1...img_uuid_ib,\n" + "\t where 'b' and 'i' are number of banks and number\n" + "\t of images in a bank respectively.\n" + ); +} + +struct fwu_mdata_object { + size_t images; + size_t banks; + size_t size; + u8 version; + size_t vsize; + void *vbuf; + struct fwu_mdata *mdata; +}; + +static int previous_bank, active_bank; +static bool __use_guid; + +static bool supported_mdata_version(unsigned long version) +{ + switch (version) { + case 1: + case 2: + return true; + default: + return false; + } +} + +static struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks, + u8 version, size_t vendor_size) +{ + struct fwu_mdata_object *mobj; + + mobj = calloc(1, sizeof(*mobj)); + if (!mobj) + return NULL; + + if (version == 1) { + mobj->size = sizeof(struct fwu_mdata) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * banks) * images; + } else { + mobj->size = sizeof(struct fwu_mdata) + + sizeof(struct fwu_fw_store_desc) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * banks) * images; + + mobj->size += vendor_size; + mobj->vsize = vendor_size; + } + + mobj->images = images; + mobj->banks = banks; + mobj->version = version; + + mobj->mdata = calloc(1, mobj->size); + if (!mobj->mdata) + goto alloc_err; + + if (vendor_size) { + mobj->vbuf = calloc(1, mobj->vsize); + if (!mobj->vbuf) + goto alloc_err; + } + + return mobj; + +alloc_err: + free(mobj->mdata); + free(mobj); + return NULL; +} + +static struct fwu_image_entry * +fwu_get_image(struct fwu_mdata_object *mobj, size_t idx) +{ + size_t offset; + + if (mobj->version == 1) { + offset = sizeof(struct fwu_mdata) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * mobj->banks) * + idx; + } else { + offset = sizeof(struct fwu_mdata) + + sizeof(struct fwu_fw_store_desc) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * mobj->banks) * + idx; + } + + return (struct fwu_image_entry *)((char *)mobj->mdata + offset); +} + +static struct fwu_image_bank_info * +fwu_get_bank(struct fwu_mdata_object *mobj, size_t img_idx, size_t bnk_idx) +{ + size_t offset; + + if (mobj->version == 1) { + offset = sizeof(struct fwu_mdata) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * mobj->banks) * + img_idx + sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * bnk_idx; + } else { + offset = sizeof(struct fwu_mdata) + + sizeof(struct fwu_fw_store_desc) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * mobj->banks) * + img_idx + sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * bnk_idx; + } + + return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset); +} + +/** + * convert_uuid_to_guid() - convert UUID to GUID + * @buf: UUID binary + * + * UUID and GUID have the same data structure, but their binary + * formats are different due to the endianness. See lib/uuid.c. + * Since uuid_parse() can handle only UUID, this function must + * be called to get correct data for GUID when parsing a string. + * + * The correct data will be returned in @buf. + */ +static void convert_uuid_to_guid(unsigned char *buf) +{ + unsigned char c; + + c = buf[0]; + buf[0] = buf[3]; + buf[3] = c; + c = buf[1]; + buf[1] = buf[2]; + buf[2] = c; + + c = buf[4]; + buf[4] = buf[5]; + buf[5] = c; + + c = buf[6]; + buf[6] = buf[7]; + buf[7] = c; +} + +static int uuid_guid_parse(char *uuidstr, unsigned char *uuid) +{ + int ret; + + ret = uuid_parse(uuidstr, uuid); + if (ret < 0) + return ret; + + if (__use_guid) + convert_uuid_to_guid(uuid); + + return ret; +} + +static int +fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj, + size_t idx, char *uuids) +{ + struct fwu_image_entry *image = fwu_get_image(mobj, idx); + struct fwu_image_bank_info *bank; + char *p = uuids, *uuid; + int i; + + if (!image) + return -ENOENT; + + /* Image location UUID */ + uuid = strsep(&p, ","); + if (!uuid) + return -EINVAL; + + if (strcmp(uuid, "0") && + uuid_guid_parse(uuid, (unsigned char *)&image->location_guid) < 0) + return -EINVAL; + + /* Image type UUID */ + uuid = strsep(&p, ","); + if (!uuid) + return -EINVAL; + + if (uuid_guid_parse(uuid, (unsigned char *)&image->image_type_guid) < 0) + return -EINVAL; + + /* Fill bank image-UUID */ + for (i = 0; i < mobj->banks; i++) { + bank = fwu_get_bank(mobj, idx, i); + if (!bank) + return -ENOENT; + bank->accepted = 1; + uuid = strsep(&p, ","); + if (!uuid) + return -EINVAL; + + if (strcmp(uuid, "0") && + uuid_guid_parse(uuid, (unsigned char *)&bank->image_guid) < 0) + return -EINVAL; + } + return 0; +} + +#if defined(CONFIG_FWU_MDATA_V1) +static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) +{ +} +#else +static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) +{ + int i; + struct fwu_fw_store_desc *fw_desc; + struct fwu_mdata *mdata = mobj->mdata; + + mdata->metadata_size = mobj->size; + mdata->desc_offset = sizeof(struct fwu_mdata); + + for (i = 0; i < MAX_BANKS_V2; i++) + mdata->bank_state[i] = i < mobj->banks ? + BANK_ACCEPTED : BANK_INVALID; + + fw_desc = (struct fwu_fw_store_desc *)((u8 *)mdata + sizeof(*mdata)); + fw_desc->num_banks = mobj->banks; + fw_desc->num_images = mobj->images; + fw_desc->img_entry_size = sizeof(struct fwu_image_entry) + + (sizeof(struct fwu_image_bank_info) * mobj->banks); + fw_desc->bank_info_entry_size = + sizeof(struct fwu_image_bank_info); +} +#endif /* CONFIG_FWU_MDATA_V1 */ + +/* Caller must ensure that @uuids[] has @mobj->images entries. */ +static int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[]) +{ + struct fwu_mdata *mdata = mobj->mdata; + char *vdata; + int i, ret; + + mdata->version = mobj->version; + mdata->active_index = active_bank; + mdata->previous_active_index = previous_bank; + + fwu_fill_version_specific_mdata(mobj); + + for (i = 0; i < mobj->images; i++) { + ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]); + if (ret < 0) + return ret; + } + + if (mobj->vsize) { + vdata = (char *)mobj->mdata + (mobj->size - mobj->vsize); + memcpy(vdata, mobj->vbuf, mobj->vsize); + } + + mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version, + mobj->size - sizeof(uint32_t)); + + return 0; +} + +static int fwu_read_vendor_data(struct fwu_mdata_object *mobj, + const char *vendor_file) +{ + int ret = 0; + FILE *vfile = NULL; + + vfile = fopen(vendor_file, "r"); + if (!vfile) { + ret = -1; + goto out; + } + + if (fread(mobj->vbuf, 1, mobj->vsize, vfile) != mobj->vsize) + ret = -1; + +out: + fclose(vfile); + return ret; +} + +static int fwu_make_mdata(size_t images, size_t banks, u8 version, + const char *vendor_file, char *uuids[], + char *output) +{ + int ret; + FILE *file; + struct stat sbuf; + size_t vendor_size = 0; + struct fwu_mdata_object *mobj; + + if (vendor_file) { + ret = stat(vendor_file, &sbuf); + if (ret) + return -errno; + + vendor_size = sbuf.st_size; + } + + mobj = fwu_alloc_mdata(images, banks, version, vendor_size); + if (!mobj) + return -ENOMEM; + + if (vendor_file) { + ret = fwu_read_vendor_data(mobj, vendor_file); + if (ret) + goto done_make; + } + + ret = fwu_parse_fill_uuids(mobj, uuids); + if (ret < 0) + goto done_make; + + file = fopen(output, "w"); + if (!file) { + ret = -errno; + goto done_make; + } + + ret = fwrite(mobj->mdata, 1, mobj->size, file); + if (ret != mobj->size) + ret = -errno; + else + ret = 0; + + fclose(file); + +done_make: + free(mobj->mdata); + free(mobj->vbuf); + free(mobj); + + return ret; +} + +int main(int argc, char *argv[]) +{ + unsigned long banks = 0, images = 0, version = 0; + int c, ret; + const char *vendor_file; + + /* Explicitly initialize defaults */ + active_bank = 0; + __use_guid = false; + previous_bank = INT_MAX; + vendor_file = NULL; + + do { + c = getopt_long(argc, argv, opts_short, options, NULL); + switch (c) { + case 'h': + print_usage(); + return 0; + case 'b': + banks = strtoul(optarg, NULL, 0); + break; + case 'i': + images = strtoul(optarg, NULL, 0); + break; + case 'g': + __use_guid = true; + break; + case 'p': + previous_bank = strtoul(optarg, NULL, 0); + break; + case 'a': + active_bank = strtoul(optarg, NULL, 0); + break; + case 'v': + version = strtoul(optarg, NULL, 0); + break; + case 'V': + vendor_file = optarg; + break; + } + } while (c != -1); + + if (!banks || !images) { + fprintf(stderr, "Error: The number of banks and images must not be 0.\n"); + return -EINVAL; + } + + if (!version || !supported_mdata_version(version)) { + fprintf(stderr, "Error: Version value can only be either 1 or 2, not %ld.\n", + version); + return -EINVAL; + } + + if (version == 1 && vendor_file) { + fprintf(stderr, "Error: Vendor Data can only be appended in version 2 of FWU Metadata.\n"); + return -EINVAL; + } + + /* This command takes UUIDs * images and output file. */ + if (optind + images + 1 != argc) { + fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n"); + print_usage(); + return -ERANGE; + } + + if (previous_bank == INT_MAX) { + /* set to the earlier bank in round-robin scheme */ + previous_bank = active_bank > 0 ? active_bank - 1 : banks - 1; + } + + ret = fwu_make_mdata(images, banks, (u8)version, vendor_file, + argv + optind, argv[argc - 1]); + if (ret < 0) + fprintf(stderr, "Error: Failed to parse and write image: %s\n", + strerror(-ret)); + + return ret; +} diff --git a/tools/mkfwumdata.c b/tools/mkfwumdata.c deleted file mode 100644 index fbc2067bc12..00000000000 --- a/tools/mkfwumdata.c +++ /dev/null @@ -1,493 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2023, Linaro Limited - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef uint8_t u8; -typedef int16_t s16; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -#undef CONFIG_FWU_NUM_BANKS -#undef CONFIG_FWU_NUM_IMAGES_PER_BANK - -/* This will dynamically allocate the fwu_mdata */ -#define CONFIG_FWU_NUM_BANKS 0 -#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0 - -/* version 2 supports maximum of 4 banks */ -#define MAX_BANKS_V2 4 - -#define BANK_INVALID (u8)0xFF -#define BANK_ACCEPTED (u8)0xFC - -#include - -static const char *opts_short = "b:i:a:p:v:V:gh"; - -static struct option options[] = { - {"banks", required_argument, NULL, 'b'}, - {"images", required_argument, NULL, 'i'}, - {"guid", required_argument, NULL, 'g'}, - {"active-bank", required_argument, NULL, 'a'}, - {"previous-bank", required_argument, NULL, 'p'}, - {"version", required_argument, NULL, 'v'}, - {"vendor-file", required_argument, NULL, 'V'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}, -}; - -static void print_usage(void) -{ - fprintf(stderr, "Usage: mkfwumdata [options] \n"); - fprintf(stderr, "Options:\n" - "\t-i, --images Number of images (mandatory)\n" - "\t-b, --banks Number of banks (mandatory)\n" - "\t-v, --version Metadata version (mandatory)\n" - "\t-a, --active-bank Active bank (default=0)\n" - "\t-p, --previous-bank Previous active bank (default=active_bank - 1)\n" - "\t-g, --guid Use GUID instead of UUID\n" - "\t-V, --vendor-file Vendor data file to append to the metadata\n" - "\t-h, --help print a help message\n" - ); - fprintf(stderr, " UUIDs list syntax:\n" - "\t ,,\n" - "\t images uuid list syntax:\n" - "\t img_uuid_00,img_uuid_01...img_uuid_0b,\n" - "\t img_uuid_10,img_uuid_11...img_uuid_1b,\n" - "\t ...,\n" - "\t img_uuid_i0,img_uuid_i1...img_uuid_ib,\n" - "\t where 'b' and 'i' are number of banks and number\n" - "\t of images in a bank respectively.\n" - ); -} - -struct fwu_mdata_object { - size_t images; - size_t banks; - size_t size; - u8 version; - size_t vsize; - void *vbuf; - struct fwu_mdata *mdata; -}; - -static int previous_bank, active_bank; -static bool __use_guid; - -static bool supported_mdata_version(unsigned long version) -{ - switch (version) { - case 1: - case 2: - return true; - default: - return false; - } -} - -static struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks, - u8 version, size_t vendor_size) -{ - struct fwu_mdata_object *mobj; - - mobj = calloc(1, sizeof(*mobj)); - if (!mobj) - return NULL; - - if (version == 1) { - mobj->size = sizeof(struct fwu_mdata) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * banks) * images; - } else { - mobj->size = sizeof(struct fwu_mdata) + - sizeof(struct fwu_fw_store_desc) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * banks) * images; - - mobj->size += vendor_size; - mobj->vsize = vendor_size; - } - - mobj->images = images; - mobj->banks = banks; - mobj->version = version; - - mobj->mdata = calloc(1, mobj->size); - if (!mobj->mdata) - goto alloc_err; - - if (vendor_size) { - mobj->vbuf = calloc(1, mobj->vsize); - if (!mobj->vbuf) - goto alloc_err; - } - - return mobj; - -alloc_err: - free(mobj->mdata); - free(mobj); - return NULL; -} - -static struct fwu_image_entry * -fwu_get_image(struct fwu_mdata_object *mobj, size_t idx) -{ - size_t offset; - - if (mobj->version == 1) { - offset = sizeof(struct fwu_mdata) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - idx; - } else { - offset = sizeof(struct fwu_mdata) + - sizeof(struct fwu_fw_store_desc) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - idx; - } - - return (struct fwu_image_entry *)((char *)mobj->mdata + offset); -} - -static struct fwu_image_bank_info * -fwu_get_bank(struct fwu_mdata_object *mobj, size_t img_idx, size_t bnk_idx) -{ - size_t offset; - - if (mobj->version == 1) { - offset = sizeof(struct fwu_mdata) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - img_idx + sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * bnk_idx; - } else { - offset = sizeof(struct fwu_mdata) + - sizeof(struct fwu_fw_store_desc) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - img_idx + sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * bnk_idx; - } - - return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset); -} - -/** - * convert_uuid_to_guid() - convert UUID to GUID - * @buf: UUID binary - * - * UUID and GUID have the same data structure, but their binary - * formats are different due to the endianness. See lib/uuid.c. - * Since uuid_parse() can handle only UUID, this function must - * be called to get correct data for GUID when parsing a string. - * - * The correct data will be returned in @buf. - */ -static void convert_uuid_to_guid(unsigned char *buf) -{ - unsigned char c; - - c = buf[0]; - buf[0] = buf[3]; - buf[3] = c; - c = buf[1]; - buf[1] = buf[2]; - buf[2] = c; - - c = buf[4]; - buf[4] = buf[5]; - buf[5] = c; - - c = buf[6]; - buf[6] = buf[7]; - buf[7] = c; -} - -static int uuid_guid_parse(char *uuidstr, unsigned char *uuid) -{ - int ret; - - ret = uuid_parse(uuidstr, uuid); - if (ret < 0) - return ret; - - if (__use_guid) - convert_uuid_to_guid(uuid); - - return ret; -} - -static int -fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj, - size_t idx, char *uuids) -{ - struct fwu_image_entry *image = fwu_get_image(mobj, idx); - struct fwu_image_bank_info *bank; - char *p = uuids, *uuid; - int i; - - if (!image) - return -ENOENT; - - /* Image location UUID */ - uuid = strsep(&p, ","); - if (!uuid) - return -EINVAL; - - if (strcmp(uuid, "0") && - uuid_guid_parse(uuid, (unsigned char *)&image->location_guid) < 0) - return -EINVAL; - - /* Image type UUID */ - uuid = strsep(&p, ","); - if (!uuid) - return -EINVAL; - - if (uuid_guid_parse(uuid, (unsigned char *)&image->image_type_guid) < 0) - return -EINVAL; - - /* Fill bank image-UUID */ - for (i = 0; i < mobj->banks; i++) { - bank = fwu_get_bank(mobj, idx, i); - if (!bank) - return -ENOENT; - bank->accepted = 1; - uuid = strsep(&p, ","); - if (!uuid) - return -EINVAL; - - if (strcmp(uuid, "0") && - uuid_guid_parse(uuid, (unsigned char *)&bank->image_guid) < 0) - return -EINVAL; - } - return 0; -} - -#if defined(CONFIG_FWU_MDATA_V1) -static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) -{ -} -#else -static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) -{ - int i; - struct fwu_fw_store_desc *fw_desc; - struct fwu_mdata *mdata = mobj->mdata; - - mdata->metadata_size = mobj->size; - mdata->desc_offset = sizeof(struct fwu_mdata); - - for (i = 0; i < MAX_BANKS_V2; i++) - mdata->bank_state[i] = i < mobj->banks ? - BANK_ACCEPTED : BANK_INVALID; - - fw_desc = (struct fwu_fw_store_desc *)((u8 *)mdata + sizeof(*mdata)); - fw_desc->num_banks = mobj->banks; - fw_desc->num_images = mobj->images; - fw_desc->img_entry_size = sizeof(struct fwu_image_entry) + - (sizeof(struct fwu_image_bank_info) * mobj->banks); - fw_desc->bank_info_entry_size = - sizeof(struct fwu_image_bank_info); -} -#endif /* CONFIG_FWU_MDATA_V1 */ - -/* Caller must ensure that @uuids[] has @mobj->images entries. */ -static int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[]) -{ - struct fwu_mdata *mdata = mobj->mdata; - char *vdata; - int i, ret; - - mdata->version = mobj->version; - mdata->active_index = active_bank; - mdata->previous_active_index = previous_bank; - - fwu_fill_version_specific_mdata(mobj); - - for (i = 0; i < mobj->images; i++) { - ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]); - if (ret < 0) - return ret; - } - - if (mobj->vsize) { - vdata = (char *)mobj->mdata + (mobj->size - mobj->vsize); - memcpy(vdata, mobj->vbuf, mobj->vsize); - } - - mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version, - mobj->size - sizeof(uint32_t)); - - return 0; -} - -static int fwu_read_vendor_data(struct fwu_mdata_object *mobj, - const char *vendor_file) -{ - int ret = 0; - FILE *vfile = NULL; - - vfile = fopen(vendor_file, "r"); - if (!vfile) { - ret = -1; - goto out; - } - - if (fread(mobj->vbuf, 1, mobj->vsize, vfile) != mobj->vsize) - ret = -1; - -out: - fclose(vfile); - return ret; -} - -static int fwu_make_mdata(size_t images, size_t banks, u8 version, - const char *vendor_file, char *uuids[], - char *output) -{ - int ret; - FILE *file; - struct stat sbuf; - size_t vendor_size = 0; - struct fwu_mdata_object *mobj; - - if (vendor_file) { - ret = stat(vendor_file, &sbuf); - if (ret) - return -errno; - - vendor_size = sbuf.st_size; - } - - mobj = fwu_alloc_mdata(images, banks, version, vendor_size); - if (!mobj) - return -ENOMEM; - - if (vendor_file) { - ret = fwu_read_vendor_data(mobj, vendor_file); - if (ret) - goto done_make; - } - - ret = fwu_parse_fill_uuids(mobj, uuids); - if (ret < 0) - goto done_make; - - file = fopen(output, "w"); - if (!file) { - ret = -errno; - goto done_make; - } - - ret = fwrite(mobj->mdata, 1, mobj->size, file); - if (ret != mobj->size) - ret = -errno; - else - ret = 0; - - fclose(file); - -done_make: - free(mobj->mdata); - free(mobj->vbuf); - free(mobj); - - return ret; -} - -int main(int argc, char *argv[]) -{ - unsigned long banks = 0, images = 0, version = 0; - int c, ret; - const char *vendor_file; - - /* Explicitly initialize defaults */ - active_bank = 0; - __use_guid = false; - previous_bank = INT_MAX; - vendor_file = NULL; - - do { - c = getopt_long(argc, argv, opts_short, options, NULL); - switch (c) { - case 'h': - print_usage(); - return 0; - case 'b': - banks = strtoul(optarg, NULL, 0); - break; - case 'i': - images = strtoul(optarg, NULL, 0); - break; - case 'g': - __use_guid = true; - break; - case 'p': - previous_bank = strtoul(optarg, NULL, 0); - break; - case 'a': - active_bank = strtoul(optarg, NULL, 0); - break; - case 'v': - version = strtoul(optarg, NULL, 0); - break; - case 'V': - vendor_file = optarg; - break; - } - } while (c != -1); - - if (!banks || !images) { - fprintf(stderr, "Error: The number of banks and images must not be 0.\n"); - return -EINVAL; - } - - if (!version || !supported_mdata_version(version)) { - fprintf(stderr, "Error: Version value can only be either 1 or 2, not %ld.\n", - version); - return -EINVAL; - } - - if (version == 1 && vendor_file) { - fprintf(stderr, "Error: Vendor Data can only be appended in version 2 of FWU Metadata.\n"); - return -EINVAL; - } - - /* This command takes UUIDs * images and output file. */ - if (optind + images + 1 != argc) { - fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n"); - print_usage(); - return -ERANGE; - } - - if (previous_bank == INT_MAX) { - /* set to the earlier bank in round-robin scheme */ - previous_bank = active_bank > 0 ? active_bank - 1 : banks - 1; - } - - ret = fwu_make_mdata(images, banks, (u8)version, vendor_file, - argv + optind, argv[argc - 1]); - if (ret < 0) - fprintf(stderr, "Error: Failed to parse and write image: %s\n", - strerror(-ret)); - - return ret; -} -- cgit v1.2.3 From bcae1381f88f2122658b3aaacb04b4ea2a873a36 Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 23 Feb 2026 14:18:37 +0100 Subject: tools: mkfwumdata: Improve error message specificity Replace the generic error message with a more informative one. This helps users quickly understand the correct command-line argument format when the tool reports an error. Signed-off-by: Kory Maincent Tested-by: Dario Binacchi Signed-off-by: Ilias Apalodimas --- tools/fwumdata_src/mkfwumdata.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/fwumdata_src/mkfwumdata.c b/tools/fwumdata_src/mkfwumdata.c index fbc2067bc12..5ceec7a2980 100644 --- a/tools/fwumdata_src/mkfwumdata.c +++ b/tools/fwumdata_src/mkfwumdata.c @@ -473,7 +473,9 @@ int main(int argc, char *argv[]) /* This command takes UUIDs * images and output file. */ if (optind + images + 1 != argc) { - fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n"); + fprintf(stderr, + "Error: Expected %ld UUID string(s) and 1 output file, got %d argument(s).\n", + images, argc - optind); print_usage(); return -ERANGE; } -- cgit v1.2.3 From e3757929646f102070232efd86d3a308b0f54ec3 Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 23 Feb 2026 14:18:38 +0100 Subject: tools: mkfwumdata: Add bank count validation for FWU metadata v2 The FWU metadata specification version 2 supports a maximum of 4 banks. Add validation to enforce this limit and prevent creation of non-compliant metadata structures when using version 2. Without this check, users could inadvertently create invalid metadata by specifying more than 4 banks, leading to potential compatibility issues with FWU-compliant firmware update implementations. Signed-off-by: Kory Maincent Tested-by: Dario Binacchi Signed-off-by: Ilias Apalodimas --- tools/fwumdata_src/mkfwumdata.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/fwumdata_src/mkfwumdata.c b/tools/fwumdata_src/mkfwumdata.c index 5ceec7a2980..8b25539fd57 100644 --- a/tools/fwumdata_src/mkfwumdata.c +++ b/tools/fwumdata_src/mkfwumdata.c @@ -471,6 +471,12 @@ int main(int argc, char *argv[]) return -EINVAL; } + if (version == 2 && banks > MAX_BANKS_V2) { + fprintf(stderr, "Error: Version 2 supports maximum %d banks, %ld requested.\n", + MAX_BANKS_V2, banks); + return -EINVAL; + } + /* This command takes UUIDs * images and output file. */ if (optind + images + 1 != argc) { fprintf(stderr, -- cgit v1.2.3 From 02673659e81391729792ebc1b816d974de3c9abf Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 23 Feb 2026 14:18:39 +0100 Subject: tools: Add support for fwumdata tool 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 Tested-by: Dario Binacchi Signed-off-by: Ilias Apalodimas --- 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 | 3 + 9 files changed, 1269 insertions(+), 1 deletion(-) create mode 100644 doc/fwumdata.1 create mode 100644 tools/fwumdata_src/fwumdata.c create mode 100644 tools/fwumdata_src/fwumdata.config create mode 100644 tools/fwumdata_src/fwumdata.h diff --git a/MAINTAINERS b/MAINTAINERS index a925247b0cb..fb9c28c33ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1244,11 +1244,15 @@ F: drivers/watchdog/sbsa_gwdt.c FWU Multi Bank Update M: Sughosh Ganu +M: Kory Maincent 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 +.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 + * + * 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 - Use custom config file + * fwumdata -a - Set active bank + * fwumdata -p - Set previous bank + * fwumdata -s - Set bank state (V2 only) + * fwumdata -i -b -A - Accept image + * fwumdata -i -b -C - Clear image acceptance + * fwumdata -i -b + * -B + * -I -C - Clear image acceptance (V1 only) + * fwumdata -l - List detailed info with GUIDs + */ + +#include +#include +#include +#include +#include +#include +#include +#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 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 Set active bank index\n" + "\t-p, --previous Set previous bank index\n" + "\t-s, --state Set bank state (V2 only)\n" + "\t state: accepted|valid|invalid\n" + "\t-i, --image Image number (for -A/-C)\n" + "\t-b, --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 Number of banks (required for V1 metadata)\n" + "\t-I, --nimages 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 and -b \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: +# +# 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 + */ + +#ifndef _FWUMDATA_H_ +#define _FWUMDATA_H_ + +#include + +/* 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..e91d72bf3e8 100644 --- a/tools/fwumdata_src/fwumdata.mk +++ b/tools/fwumdata_src/fwumdata.mk @@ -5,3 +5,6 @@ mkfwumdata-objs := fwumdata_src/mkfwumdata.o generated/lib/crc32.o HOSTLDLIBS_mkfwumdata += -luuid hostprogs-always-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata + +fwumdata-objs := fwumdata_src/fwumdata.o generated/lib/crc32.o +hostprogs-always-$(CONFIG_TOOLS_FWUMDATA) += fwumdata -- cgit v1.2.3 From 44a1e17b2a49d724538b4d500b27b0eda4be015c Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 23 Feb 2026 14:18:40 +0100 Subject: tools: mkfwumdata: Remove dependency on fwu_mdata.h header The dependency on fwu_mdata.h creates unnecessary configuration requirements. To generate metadata V1, CONFIG_FWU_MDATA_V1 must be enabled, which in turn requires enabling FWU_MULTI_BANK_UPDATE, EFI_CAPSULE_ON_DISK, PARTITION_TYPE_GUID, and other unrelated configs. This is not suitable for a simple standalone tool. Additionally, even with the "-v 1" option to generate V1 metadata, the tool will still include the firmware store description if CONFIG_FWU_MDATA_V1 is not enabled. This structure should only be present in metadata V2. Replace the fwu_mdata.h dependency with the new fwumdata header to make the tool compatible with both V1 and V2 without requiring any defconfig changes. This also uses the access helper functions from the header to eliminate code duplication. Acked-by: Sughosh Ganu Tested-by: Sughosh Ganu Signed-off-by: Kory Maincent Tested-by: Dario Binacchi Signed-off-by: Ilias Apalodimas --- tools/fwumdata_src/mkfwumdata.c | 96 ++++++++--------------------------------- 1 file changed, 17 insertions(+), 79 deletions(-) diff --git a/tools/fwumdata_src/mkfwumdata.c b/tools/fwumdata_src/mkfwumdata.c index 8b25539fd57..b8b60473b91 100644 --- a/tools/fwumdata_src/mkfwumdata.c +++ b/tools/fwumdata_src/mkfwumdata.c @@ -17,26 +17,7 @@ #include #include -typedef uint8_t u8; -typedef int16_t s16; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -#undef CONFIG_FWU_NUM_BANKS -#undef CONFIG_FWU_NUM_IMAGES_PER_BANK - -/* This will dynamically allocate the fwu_mdata */ -#define CONFIG_FWU_NUM_BANKS 0 -#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0 - -/* version 2 supports maximum of 4 banks */ -#define MAX_BANKS_V2 4 - -#define BANK_INVALID (u8)0xFF -#define BANK_ACCEPTED (u8)0xFC - -#include +#include "fwumdata.h" static const char *opts_short = "b:i:a:p:v:V:gh"; @@ -116,6 +97,7 @@ static struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks, sizeof(struct fwu_image_bank_info) * banks) * images; } else { mobj->size = 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) * banks) * images; @@ -146,50 +128,6 @@ alloc_err: return NULL; } -static struct fwu_image_entry * -fwu_get_image(struct fwu_mdata_object *mobj, size_t idx) -{ - size_t offset; - - if (mobj->version == 1) { - offset = sizeof(struct fwu_mdata) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - idx; - } else { - offset = sizeof(struct fwu_mdata) + - sizeof(struct fwu_fw_store_desc) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - idx; - } - - return (struct fwu_image_entry *)((char *)mobj->mdata + offset); -} - -static struct fwu_image_bank_info * -fwu_get_bank(struct fwu_mdata_object *mobj, size_t img_idx, size_t bnk_idx) -{ - size_t offset; - - if (mobj->version == 1) { - offset = sizeof(struct fwu_mdata) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - img_idx + sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * bnk_idx; - } else { - offset = sizeof(struct fwu_mdata) + - sizeof(struct fwu_fw_store_desc) + - (sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * mobj->banks) * - img_idx + sizeof(struct fwu_image_entry) + - sizeof(struct fwu_image_bank_info) * bnk_idx; - } - - return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset); -} - /** * convert_uuid_to_guid() - convert UUID to GUID * @buf: UUID binary @@ -239,11 +177,13 @@ static int fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj, size_t idx, char *uuids) { - struct fwu_image_entry *image = fwu_get_image(mobj, idx); struct fwu_image_bank_info *bank; + struct fwu_image_entry *image; char *p = uuids, *uuid; int i; + image = fwu_get_image_entry(mobj->mdata, mobj->version, + mobj->banks, idx); if (!image) return -ENOENT; @@ -266,7 +206,8 @@ fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj, /* Fill bank image-UUID */ for (i = 0; i < mobj->banks; i++) { - bank = fwu_get_bank(mobj, idx, i); + bank = fwu_get_bank_info(mobj->mdata, mobj->version, + mobj->banks, idx, i); if (!bank) return -ENOENT; bank->accepted = 1; @@ -281,25 +222,22 @@ fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj, return 0; } -#if defined(CONFIG_FWU_MDATA_V1) -static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) -{ -} -#else static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) { int i; struct fwu_fw_store_desc *fw_desc; - struct fwu_mdata *mdata = mobj->mdata; + struct fwu_mdata_ext *mdata_ext; - mdata->metadata_size = mobj->size; - mdata->desc_offset = sizeof(struct fwu_mdata); + mdata_ext = fwu_get_fw_mdata_ext(mobj->mdata); + mdata_ext->metadata_size = mobj->size; + mdata_ext->desc_offset = sizeof(struct fwu_mdata) + + sizeof(struct fwu_mdata_ext); for (i = 0; i < MAX_BANKS_V2; i++) - mdata->bank_state[i] = i < mobj->banks ? - BANK_ACCEPTED : BANK_INVALID; + mdata_ext->bank_state[i] = i < mobj->banks ? + FWU_BANK_ACCEPTED : FWU_BANK_INVALID; - fw_desc = (struct fwu_fw_store_desc *)((u8 *)mdata + sizeof(*mdata)); + fw_desc = fwu_get_fw_desc(mobj->mdata); fw_desc->num_banks = mobj->banks; fw_desc->num_images = mobj->images; fw_desc->img_entry_size = sizeof(struct fwu_image_entry) + @@ -307,7 +245,6 @@ static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj) fw_desc->bank_info_entry_size = sizeof(struct fwu_image_bank_info); } -#endif /* CONFIG_FWU_MDATA_V1 */ /* Caller must ensure that @uuids[] has @mobj->images entries. */ static int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[]) @@ -320,7 +257,8 @@ static int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[]) mdata->active_index = active_bank; mdata->previous_active_index = previous_bank; - fwu_fill_version_specific_mdata(mobj); + if (mdata->version == 2) + fwu_fill_version_specific_mdata(mobj); for (i = 0; i < mobj->images; i++) { ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]); -- cgit v1.2.3