diff mbox series

[v2,6/7] tools: add genguid tool

Message ID 20240529-b4-dynamic-uuid-v2-6-c26f31057bbe@linaro.org
State New
Headers show
Series efi: CapsuleUpdate: support for dynamic UUIDs | expand

Commit Message

Caleb Connolly May 29, 2024, 2:48 p.m. UTC
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.

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
---
 tools/Makefile  |   3 ++
 tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+)

Comments

Simon Glass May 29, 2024, 4:30 p.m. UTC | #1
Hi Caleb,

On Wed, 29 May 2024 at 08:49, Caleb Connolly <caleb.connolly@linaro.org> 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.
>
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> ---
>  tools/Makefile  |   3 ++
>  tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 157 insertions(+)

Where is this tool used? Can you add a man page and test?

Regards,
Simon
Caleb Connolly May 29, 2024, 7:02 p.m. UTC | #2
Hi Simon,

On 29/05/2024 18:30, Simon Glass wrote:
> Hi Caleb,
> 
> On Wed, 29 May 2024 at 08:49, Caleb Connolly <caleb.connolly@linaro.org> 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.
>>
>> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
>> ---
>>   tools/Makefile  |   3 ++
>>   tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   2 files changed, 157 insertions(+)
> 
> Where is this tool used? Can you add a man page and test?

It currently doesn't have any users, it just allows for pre-generating 
GUIDs ahead of time for a given board. It might be hooked up to vendor 
tooling or some usecase like that.

I've somehow escaped learning Groff before now heh, I can give it a go 
and write a test.
> 
> Regards,
> Simon
Simon Glass May 29, 2024, 7:45 p.m. UTC | #3
Hi Caleb,

On Wed, 29 May 2024 at 13:02, Caleb Connolly <caleb.connolly@linaro.org> wrote:
>
> Hi Simon,
>
> On 29/05/2024 18:30, Simon Glass wrote:
> > Hi Caleb,
> >
> > On Wed, 29 May 2024 at 08:49, Caleb Connolly <caleb.connolly@linaro.org> 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.
> >>
> >> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> >> ---
> >>   tools/Makefile  |   3 ++
> >>   tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>   2 files changed, 157 insertions(+)
> >
> > Where is this tool used? Can you add a man page and test?
>
> It currently doesn't have any users, it just allows for pre-generating
> GUIDs ahead of time for a given board. It might be hooked up to vendor
> tooling or some usecase like that.
>
> I've somehow escaped learning Groff before now heh, I can give it a go
> and write a test.

OK (just copy bits of mkimage.1 :-)

Re the test, would this be something that binman would use? If so you
could put a test there.

Regards,
Simon
Dom (shymega) Rodriguez May 29, 2024, 10:31 p.m. UTC | #4
On 29.05.2024 16:48, 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.
>
>Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
>---
> tools/Makefile  |   3 ++
> tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 157 insertions(+)
>
>diff --git a/tools/Makefile b/tools/Makefile
>index 6a4280e3668f..7db7723793d5 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_MKEFICAPSULE) += 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..dbac65d42623
>--- /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"
>+		"  -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);
>+}
>+
>+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);
>+}

Hi Caleb,

Consistency-wise, unless you wanted this function (`u16_strsize`) non-static,
it would be more consistent to make it static, like the `usage` function. 

Otherwise LGTM.

>+
>+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 */
>+	n_images = argc - optind - 1;
>+	images = argv + optind + 1;
>+	images_u16 = calloc(n_images, sizeof(char *));
>+	for (int i = 0; i < n_images; i++) {
>+		images_u16[i] = calloc(1, strlen(images[i]) * 2 + 2);
>+		for (int j = 0; j < strlen(images[i]); j++)
>+			images_u16[i][j] = (uint16_t)images[i][j];
>+	}
>+
>+	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;
>+}
>+
>
>-- 
>2.45.0
>
Best wishes,
--
Dom Rodriguez
GPG Fingerprint: EB0D 45E6 D0DC 1BA1 A2B5  FC24 72DC F123 1E54 BD43
Caleb Connolly May 31, 2024, 1:34 p.m. UTC | #5
Hi Simon,

On 29/05/2024 21:45, Simon Glass wrote:
> Hi Caleb,
> 
> On Wed, 29 May 2024 at 13:02, Caleb Connolly <caleb.connolly@linaro.org> wrote:
>>
>> Hi Simon,
>>
>> On 29/05/2024 18:30, Simon Glass wrote:
>>> Hi Caleb,
>>>
>>> On Wed, 29 May 2024 at 08:49, Caleb Connolly <caleb.connolly@linaro.org> 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.
>>>>
>>>> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
>>>> ---
>>>>    tools/Makefile  |   3 ++
>>>>    tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    2 files changed, 157 insertions(+)
>>>
>>> Where is this tool used? Can you add a man page and test?
>>
>> It currently doesn't have any users, it just allows for pre-generating
>> GUIDs ahead of time for a given board. It might be hooked up to vendor
>> tooling or some usecase like that.
>>
>> I've somehow escaped learning Groff before now heh, I can give it a go
>> and write a test.
> 
> OK (just copy bits of mkimage.1 :-)

Right :D
> 
> Re the test, would this be something that binman would use? If so you
> could put a test there.

This tool is just a wrapper for gen_uuid_v5() which we already test in 
the dynamic UUID unit tests also introduced in this series. I don't 
think it's necessary to additionally test this tool.

Kind regards,
> 
> Regards,
> Simon
diff mbox series

Patch

diff --git a/tools/Makefile b/tools/Makefile
index 6a4280e3668f..7db7723793d5 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_MKEFICAPSULE) += 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..dbac65d42623
--- /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"
+		"  -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);
+}
+
+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 */
+	n_images = argc - optind - 1;
+	images = argv + optind + 1;
+	images_u16 = calloc(n_images, sizeof(char *));
+	for (int i = 0; i < n_images; i++) {
+		images_u16[i] = calloc(1, strlen(images[i]) * 2 + 2);
+		for (int j = 0; j < strlen(images[i]); j++)
+			images_u16[i][j] = (uint16_t)images[i][j];
+	}
+
+	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;
+}
+