[PATCH v3 6/7] tools: add genguid tool
Heinrich Schuchardt
xypron.glpk at gmx.de
Wed Jun 5 08:38:57 CEST 2024
On 6/5/24 08:36, Heinrich Schuchardt wrote:
> On 5/31/24 15:50, Caleb Connolly wrote:
>> Add a tool that can generate GUIDs that match those generated internally
>> by U-Boot for capsule update fw_images.
>>
>> Dynamic UUIDs in U-Boot work by taking a namespace UUID and hashing it
>> with the board model, compatible, and fw_image name.
>>
>> This tool accepts the same inputs and will produce the same GUID as
>> U-Boot would at runtime.
>
> This functionality should be integrated into the mkeficapsule.
>
> Just pass the device-tree into mkeficapsule and generate the GUIDs
> whereever they are needed.
>
> Best regards
>
> Heinrich
>
>>
>> Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org>
>> ---
>> doc/genguid.1 | 52 +++++++++++++++++++
>> tools/Kconfig | 7 +++
>> tools/Makefile | 3 ++
>> tools/genguid.c | 154
A change to MAINTAINERS is missing.
Best regards
Heinrich
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 216 insertions(+)
>>
>> diff --git a/doc/genguid.1 b/doc/genguid.1
>> new file mode 100644
>> index 000000000000..4128055b3a9a
>> --- /dev/null
>> +++ b/doc/genguid.1
>> @@ -0,0 +1,52 @@
>> +.\" SPDX-License-Identifier: GPL-2.0+
>> +.\" Copyright (c) 2024, Linaro Limited
>> +.TH GENGUID 1 "May 2024"
>> +
>> +.SH NAME
>> +genguid \- Generate deterministic EFI capsule image GUIDs for a board
>> +
>> +.SH SYNOPSIS
>> +.B genguid
>> +.RI GUID " " [ -vj ] " " -c " " COMPAT " " NAME...
>> +
>> +.SH "DESCRIPTION"
>> +The
>> +.B genguid
>> +command is used to determine the update image GUIDs for a board using
>> +dynamic UUIDs. The command takes a namespace GUID (defined in the boards
>> +defconfig), the boards first compatible string, and the names of the
>> +firmware images. The command will output the GUIDs for each image.
>> +
>> +As the dynamic UUID mechanism generates GUIDs at runtime, it would be
>> +necessary to actually boot U-Boot on the board and enable debug logs
>> +to retrieve the generated GUIDs. This tools just simplifies that
>> process.
>> +
>> +.SH "OPTIONS"
>> +
>> +.TP
>> +.BI GUID
>> +The namespace/salt GUID, same as CONFIG_EFI_CAPSULE_NAMESPACE_UUID.
>> +The format is:
>> + xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
>> +
>> +.TP
>> +.BI "-v\fR,\fB --verbose "
>> +Print additional information to stderr.
>> +
>> +.TP
>> +.BI "-j\fR,\fB --json "
>> +Output the results in JSON format (array of object with name/uuid
>> properties).
>> +
>> +.TP
>> +.BI "-c\fR,\fB --compat " COMPAT
>> +The first entry in the boards root compatible property.
>> +
>> +.TP
>> +.BI NAME...
>> +The names of the firmware images to generate GUIDs for (e.g.
>> "SANDBOX-UBOOT-ENV").
>> +
>> +.SH AUTHORS
>> +Written by Caleb Connolly <caleb.connolly at linaro.org>
>> +
>> +.SH HOMEPAGE
>> +https://u-boot.org
>> diff --git a/tools/Kconfig b/tools/Kconfig
>> index 667807b33173..13201ff61fd4 100644
>> --- a/tools/Kconfig
>> +++ b/tools/Kconfig
>> @@ -103,8 +103,15 @@ config TOOLS_MKEFICAPSULE
>> This command allows users to create a UEFI capsule file and,
>> optionally sign that file. If you want to enable UEFI capsule
>> update feature on your target, you certainly need this.
>>
>> +config TOOLS_GENGUID
>> + bool "Build genguid command"
>> + default y if EFI_CAPSULE_DYNAMIC_UUIDS
>
> Distros have a package u-boot-tools. You want this package to contain
> all tools.
>
> Please, ensure that the new tool is built by tools-only_defconfig.
>
>> + help
>> + This command allows users to generate the GUIDs that a given
>> + board would use for UEFI capsule update feature.
>> +
>> menuconfig FSPI_CONF_HEADER
>> bool "FlexSPI Header Configuration"
>> help
>> FSPI Header Configuration
>> diff --git a/tools/Makefile b/tools/Makefile
>> index 6a4280e3668f..29e9a93b0f24 100644
>> --- a/tools/Makefile
>> +++ b/tools/Makefile
>> @@ -253,8 +253,11 @@ HOSTLDLIBS_mkeficapsule += \
>> HOSTLDLIBS_mkeficapsule += \
>> $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
>> hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
>>
>> +genguid-objs := generated/lib/uuid.o generated/lib/sha1.o genguid.o
>> +hostprogs-$(CONFIG_TOOLS_GENGUID) += genguid
>> +
>> mkfwumdata-objs := mkfwumdata.o generated/lib/crc32.o
>> HOSTLDLIBS_mkfwumdata += -luuid
>> hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata
>>
>> diff --git a/tools/genguid.c b/tools/genguid.c
>> new file mode 100644
>> index 000000000000..e71bc1d48f95
>> --- /dev/null
>> +++ b/tools/genguid.c
>> @@ -0,0 +1,154 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright 2024 Linaro Ltd.
>> + * Author: Caleb Connolly
>> + */
>> +
>> +#include <getopt.h>
>> +#include <stdbool.h>
>> +#include <stdint.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <linux/types.h>
>> +
>> +#include <uuid.h>
>> +
>> +static struct option options[] = {
>> + {"dtb", required_argument, NULL, 'd'},
>> + {"compat", required_argument, NULL, 'c'},
>> + {"help", no_argument, NULL, 'h'},
>> + {"verbose", no_argument, NULL, 'v'},
>> + {"json", no_argument, NULL, 'j'},
>> + {NULL, 0, NULL, 0},
>> +};
>> +
>> +static void usage(const char *progname)
>> +{
>> + fprintf(stderr, "Usage: %s GUID [-v] -c COMPAT NAME...\n",
>> progname);
>> + fprintf(stderr,
>> + "Generate a v5 GUID for one of more U-Boot fw_images the same
>> way U-Boot does at runtime.\n");
>> + fprintf(stderr,
>> + "\nOptions:\n"
>> + " GUID namespace/salt GUID in 8-4-4-4-12
>> format\n"
>> + " -h, --help display this help and exit\n"
>> + " -c, --compat=COMPAT first compatible property in the
>> board devicetree\n"
>
> We don't need the first compatible string but the one in the root node.
>
>> + " -v, --verbose print debug messages\n"
>> + " -j, --json output in JSON format\n"
>> + " NAME... one or more names of fw_images to
>> generate GUIDs for\n"
>> + );
>> + fprintf(stderr, "\nExample:\n");
>> + fprintf(stderr, " %s 2a5aa852-b856-4d97-baa9-5c5f4421551f \\\n"
>> + "\t-c \"qcom,qrb4210-rb2\" \\\n"
>> + "\tQUALCOMM-UBOOT\n", progname);
>> +}
>> +
>> +static size_t u16_strsize(const uint16_t *in)
>> +{
>> + size_t i = 0, count = UINT16_MAX;
>> +
>> + while (count-- && in[i])
>> + i++;
>> +
>> + return (i + 1) * sizeof(uint16_t);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> + struct uuid namespace;
>> + char *namespace_str;
>> + char uuid_str[37];
>> + char **image_uuids;
>> + char *compatible = NULL;
>> + uint16_t **images_u16;
>> + char **images;
>> + int c, n_images;
>> + bool debug = false, json = false;
>> +
>> + if (argc < 2) {
>> + usage(argv[0]);
>> + return 1;
>> + }
>> +
>> + namespace_str = argv[1];
>> +
>> + /* The first arg is the GUID so skip it */
>> + while ((c = getopt_long(argc, argv, "c:hvj", options, NULL)) !=
>> -1) {
>> + switch (c) {
>> + case 'c':
>> + compatible = strdup(optarg);
>> + break;
>> + case 'h':
>> + usage(argv[0]);
>> + return 0;
>> + case 'v':
>> + debug = true;
>> + break;
>> + case 'j':
>> + json = true;
>> + break;
>> + default:
>> + usage(argv[0]);
>> + return 1;
>> + }
>> + }
>> +
>> + if (!compatible) {
>> + fprintf(stderr, "ERROR: Please specify the compatible
>> property.\n\n");
>> + usage(argv[0]);
>> + return 1;
>> + }
>> +
>> + if (uuid_str_to_bin(namespace_str, (unsigned char *)&namespace,
>> UUID_STR_FORMAT_GUID)) {
>> + fprintf(stderr, "ERROR: Check that your UUID is formatted
>> correctly.\n");
>> + exit(EXIT_FAILURE);
>> + }
>> +
>> + /* This is probably not the best way to convert a string to a
>> "u16" string */
>
> Do you mean UTF-16?
>
> Instead of writing "not the best way", please, describe the restrictions.
>
>> + n_images = argc - optind - 1;
>> + images = argv + optind + 1;
>> + images_u16 = calloc(n_images, sizeof(char *));
>
> Please, check that the result in not NULL.
>
>> + for (int i = 0; i < n_images; i++) {
>> + images_u16[i] = calloc(1, strlen(images[i]) * 2 + 2);
>
> ditto
>
>> + for (int j = 0; j < strlen(images[i]); j++)
>> + images_u16[i][j] = (uint16_t)images[i][j];
>
> This is definitively not working for non-ASCII characters. You should
> throw an error for non-ASCII or provide a conversion routine.
>
> Best regards
>
> Heinrich
>
>> + }
>> +
>> + if (debug) {
>> + fprintf(stderr, "GUID: ");
>> + uuid_bin_to_str((uint8_t *)&namespace, uuid_str,
>> UUID_STR_FORMAT_GUID);
>> + fprintf(stderr, "%s\n", uuid_str);
>> + fprintf(stderr, "Compatible: \"%s\"\n", compatible);
>> + fprintf(stderr, "Images: ");
>> + for (int i = 0; i < n_images; i++)
>> + fprintf(stderr, "\"%s\"%s", argv[optind + i + 1],
>> + i == n_images - 1 ? "\n" : ", ");
>> + }
>> +
>> + image_uuids = calloc(n_images, sizeof(char *));
>> + for (int i = 0; i < n_images; i++) {
>> + struct uuid image_type_id;
>> +
>> + gen_uuid_v5(&namespace, &image_type_id,
>> + compatible, strlen(compatible),
>> + images_u16[i], u16_strsize(images_u16[i]) -
>> sizeof(uint16_t),
>> + NULL);
>> +
>> + uuid_bin_to_str((uint8_t *)&image_type_id, uuid_str,
>> UUID_STR_FORMAT_GUID);
>> + image_uuids[i] = strdup(uuid_str);
>> + }
>> +
>> + if (json) {
>> + printf("[\n");
>> + for (int i = 0; i < n_images; i++)
>> + printf("\t{\"name\": \"%s\", \"uuid\": \"%s\"}%s\n",
>> images[i], image_uuids[i],
>> + i == n_images - 1 ? "" : ",");
>> + printf("]\n");
>> + } else {
>> + for (int i = 0; i < n_images; i++)
>> + printf("%-24s| %s\n", images[i], image_uuids[i]);
>> + }
>> +
>> + return 0;
>> +}
>> +
>>
>
More information about the U-Boot
mailing list