Message ID | 20240531-b4-dynamic-uuid-v3-6-ca4a4865db00@linaro.org |
---|---|
State | New |
Headers | show |
Series | efi: CapsuleUpdate: support for dynamic UUIDs | expand |
Hi Caleb, On Fri, 31 May 2024 at 07:50, 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> > --- > doc/genguid.1 | 52 +++++++++++++++++++ > tools/Kconfig | 7 +++ > tools/Makefile | 3 ++ > tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 216 insertions(+) Reviewed-by: Simon Glass <sjg@chromium.org> Can I suggest you look at patman, which will provide a per-patch change list? Regards, SImon
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@linaro.org> > --- > doc/genguid.1 | 52 +++++++++++++++++++ > tools/Kconfig | 7 +++ > tools/Makefile | 3 ++ > tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 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@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; > +} > + >
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@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@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; >> +} >> + >> >
Hi Heinrich On Wed, 5 Jun 2024 at 09:36, Heinrich Schuchardt <xypron.glpk@gmx.de> 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. I like the idea of moving this mkeficapsule which is already part of distro packages but, isn't it easier to pass the compatible string node and the namespace? Thanks /Ilias > > Best regards > > Heinrich [...] Thanks /Ilias
On 05/06/2024 11:25, Ilias Apalodimas wrote: > Hi Heinrich > > On Wed, 5 Jun 2024 at 09:36, Heinrich Schuchardt <xypron.glpk@gmx.de> 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. > > I like the idea of moving this mkeficapsule which is already part of > distro packages but, isn't it easier to pass the compatible string > node and the namespace? Yeah, we only need the first entry in the compatible property, so parsing the whole DT is a bit overkill. I can look at combining these tools, can mkeficapsule generate a capsule for multiple images? Would we want it to work such that you provide the namespace GUID, compatible, and image names, and have it generate a capsule with the right image GUIDs? Or would it be simple enough to just incorporate the functionality of genguid? Kind regards, > > Thanks > /Ilias >> >> Best regards >> >> Heinrich > > [...] > > Thanks > /Ilias
+ CC Sughosh On Wed, 5 Jun 2024 at 15:29, Caleb Connolly <caleb.connolly@linaro.org> wrote: > > > > On 05/06/2024 11:25, Ilias Apalodimas wrote: > > Hi Heinrich > > > > On Wed, 5 Jun 2024 at 09:36, Heinrich Schuchardt <xypron.glpk@gmx.de> 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. > > > > I like the idea of moving this mkeficapsule which is already part of > > distro packages but, isn't it easier to pass the compatible string > > node and the namespace? > > Yeah, we only need the first entry in the compatible property, so > parsing the whole DT is a bit overkill. > > I can look at combining these tools, can mkeficapsule generate a capsule > for multiple images? Not yet. There's [0] which adds that functionality but we are discussing parsing a .yaml file instead of a custom format > > Would we want it to work such that you provide the namespace GUID, > compatible, and image names, and have it generate a capsule with the > right image GUIDs? > > Or would it be simple enough to just incorporate the functionality of > genguid? > I'd argue for both since people might choose other tools to generate a capsule. So you want 2 flags ideally (random flags) -x: dump the autogenerated GUIDs -y use the autogenerated GUIDs on capsule generation Since mkeficapsule can't generate a capsule with multiple payloads yet, you will be limited to just one, but I guess it's easy to expand it once [0] is merged [0] https://lore.kernel.org/u-boot/20240419065542.1160527-1-sughosh.ganu@linaro.org/ Thanks /Ilias
On Fri, May 31, 2024 at 03:50:40PM +0200, Caleb Connolly wrote: > Add a tool that can generate GUIDs that match those generated internally > by U-Boot for capsule update fw_images. Hi Caleb, Thanks for working on this. I think there is a leftover "dtb" option; see below. Best regards, Vincent. > > 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> > --- > doc/genguid.1 | 52 +++++++++++++++++++ > tools/Kconfig | 7 +++ > tools/Makefile | 3 ++ > tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 216 insertions(+) (...) > 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'}, I think this is unused. > + {"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); > +} > + > +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 */ > + 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 >
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@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 + 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" + " -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 */ + 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; +} +
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> --- doc/genguid.1 | 52 +++++++++++++++++++ tools/Kconfig | 7 +++ tools/Makefile | 3 ++ tools/genguid.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+)