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.conno...@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.conno...@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;
+}
+



Reply via email to