Message ID | 20240809-b4-dynamic-uuid-v7-9-8c44ab1f06a5@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series | efi: CapsuleUpdate: support for dynamic UUIDs | expand |
On Fri, 9 Aug 2024 at 03:56, Caleb Connolly <caleb.connolly@linaro.org> wrote: > > Add support for generating GUIDs that match those generated internally > by U-Boot for capsule update fw_images when using dynamic UUIDs. > > Dynamic UUIDs in U-Boot work by taking a namespace UUID and hashing it > with the board compatible and fw_image name. This feature just provides > a way to determine the UUIDs for a particular board without having to > actually boot U-Boot on it. > > Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org> > --- > doc/mkeficapsule.1 | 23 ++++++++ > tools/mkeficapsule.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 174 insertions(+), 5 deletions(-) > > diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1 > index c4c2057d5c7a..bf735295effa 100644 > --- a/doc/mkeficapsule.1 > +++ b/doc/mkeficapsule.1 > @@ -9,8 +9,11 @@ mkeficapsule \- Generate EFI capsule file for U-Boot > .SH SYNOPSIS > .B mkeficapsule > .RI [ options ] " " [ image-blob ] " " capsule-file > > +.B mkeficapsule > +.RI guidgen " " [ GUID ] " " DTB " " IMAGE_NAME... > + > .SH "DESCRIPTION" > The > .B mkeficapsule > command is used to create an EFI capsule file to be used by U-Boot for firmware > @@ -41,8 +44,12 @@ format is the same as used in the new uImage format and allows for > multiple binary blobs in a single capsule file. > This type of image file can be generated by > .BR mkimage . > > +mkeficapsule can also be used to simulate the dynamic GUID generation used to > +identify firmware images in capsule updates by providing the namespace guid, dtb > +for the board, and a list of firmware images. > + > .SH "OPTIONS" > > .TP > .BI "-g\fR,\fB --guid " guid-string > @@ -112,8 +119,24 @@ at every firmware update. > .TP > .B "-d\fR,\fB --dump_sig" > Dump signature data into *.p7 file > > +.SH "GUIDGEN OPTIONS" > + > +.TP > +.B "[GUID]" > +The namespace/salt GUID, by default this is EFI_CAPSULE_NAMESPACE_GUID. > +The format is: > + xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx > + > +.TP > +.B DTB > +The device tree blob file for the board. > + > +.TP > +.B IMAGE_NAME... > +The names of the firmware images to generate GUIDs for. > + > .PP > .SH FILES > .TP > .I /EFI/UpdateCapsule > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c > index 54fb4dee3ee5..8dfc09ffaf4f 100644 > --- a/tools/mkeficapsule.c > +++ b/tools/mkeficapsule.c > @@ -19,12 +19,16 @@ > #include <gnutls/gnutls.h> > #include <gnutls/pkcs7.h> > #include <gnutls/abstract.h> > > +#include <libfdt.h> > #include <u-boot/uuid.h> > > #include "eficapsule.h" > > +// Matches CONFIG_EFI_CAPSULE_NAMESPACE_GUID > +#define DEFAULT_NAMESPACE_GUID "8c9f137e-91dc-427b-b2d6-b420faebaf2a" > + > static const char *tool_name = "mkeficapsule"; > > efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; > efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; > @@ -53,11 +57,23 @@ static struct option options[] = { > {"help", no_argument, NULL, 'h'}, > {NULL, 0, NULL, 0}, > }; > > -static void print_usage(void) > + > +static void print_usage_guidgen(void) > { > - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" > + fprintf(stderr, "%s guidgen [GUID] DTB IMAGE_NAME...\n" > + "Options:\n" > + > + "\tGUID Namespace GUID (default: %s)\n" > + "\tDTB Device Tree Blob\n" > + "\tIMAGE_NAME... One or more names of fw_images to generate GUIDs for\n", > + tool_name, DEFAULT_NAMESPACE_GUID); > +} > + > +static void print_usage_mkeficapsule(void) > +{ > + fprintf(stderr, "Usage: \n\n%s [options] <image blob> <output file>\n" > "Options:\n" > > "\t-g, --guid <guid string> guid for image blob type\n" > "\t-i, --index <index> update image index\n" > @@ -70,10 +86,11 @@ static void print_usage(void) > "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" > "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" > "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" > "\t-D, --dump-capsule dump the contents of the capsule headers\n" > - "\t-h, --help print a help message\n", > + "\t-h, --help print a help message\n\n", > tool_name); > + print_usage_guidgen(); > } > > /** > * auth_context - authentication context > @@ -816,8 +833,130 @@ static void dump_capsule_contents(char *capsule_file) > exit(EXIT_FAILURE); > } > } > > +static struct fdt_header *load_dtb(const char *path) > +{ > + struct fdt_header *dtb; > + ssize_t dtb_size; > + FILE *f; > + > + /* Open and parse DTB */ > + f = fopen(path, "r"); > + if (!f) { > + fprintf(stderr, "Cannot open %s\n", path); > + return NULL; > + } > + > + if (fseek(f, 0, SEEK_END)) { > + fprintf(stderr, "Cannot seek to the end of %s: %s\n", > + path, strerror(errno)); > + return NULL; > + } > + > + dtb_size = ftell(f); > + if (dtb_size < 0) { > + fprintf(stderr, "Cannot ftell %s: %s\n", > + path, strerror(errno)); > + return NULL; > + } > + > + fseek(f, 0, SEEK_SET); > + > + dtb = malloc(dtb_size); > + if (!dtb) { > + fprintf(stderr, "Can't allocated %ld\n", dtb_size); > + return NULL; > + } > + > + if (fread(dtb, dtb_size, 1, f) != 1) { > + fprintf(stderr, "Can't read %ld bytes from %s\n", > + dtb_size, path); > + free(dtb); > + return NULL; > + } > + > + fclose(f); > + > + return dtb; > +} > + > +#define MAX_IMAGE_NAME_LEN 128 > +static int genguid(int argc, char **argv) > +{ > + int idx = 2, ret; > + unsigned char namespace[16]; > + struct efi_guid image_type_id; > + const char *dtb_path; > + struct fdt_header *dtb; > + const char *compatible; > + int compatlen, namelen; > + uint16_t fw_image[MAX_IMAGE_NAME_LEN]; > + > + if (argc < 2) { > + fprintf(stderr, "Usage: "); > + print_usage_guidgen(); > + return -1; > + } > + > + if (uuid_str_to_bin(argv[1], namespace, UUID_STR_FORMAT_GUID)) { > + uuid_str_to_bin(DEFAULT_NAMESPACE_GUID, namespace, UUID_STR_FORMAT_GUID); > + dtb_path = argv[1]; > + } else { > + dtb_path = argv[2]; > + idx = 3; > + } > + > + if (idx == argc) { > + fprintf(stderr, "Usage: "); > + print_usage_guidgen(); > + return -1; > + } > + > + dtb = load_dtb(dtb_path); > + if (!dtb) > + return -1; > + > + if ((ret = fdt_check_header(dtb))) { > + fprintf(stderr, "Invalid DTB header: %d\n", ret); > + return -1; > + } > + > + compatible = fdt_getprop(dtb, 0, "compatible", &compatlen); > + if (!compatible) { > + fprintf(stderr, "No compatible string found in DTB\n"); > + return -1; > + } > + if (strnlen(compatible, compatlen) >= compatlen) { > + fprintf(stderr, "Compatible string not null-terminated\n"); > + return -1; > + } > + > + printf("Generating GUIDs for %s with namespace %s:\n", > + compatible, DEFAULT_NAMESPACE_GUID); > + for (; idx < argc; idx++) { > + memset(fw_image, 0, sizeof(fw_image)); > + namelen = strlen(argv[idx]); > + if (namelen > MAX_IMAGE_NAME_LEN) { > + fprintf(stderr, "Image name too long: %s\n", argv[idx]); > + return -1; > + } > + > + for (int i = 0; i < namelen; i++) > + fw_image[i] = (uint16_t)argv[idx][i]; > + > + gen_v5_guid((struct uuid *)&namespace, &image_type_id, > + compatible, strlen(compatible), > + fw_image, namelen * sizeof(uint16_t), > + NULL); > + > + printf("%s: ", argv[idx]); > + print_guid(&image_type_id); > + } > + > + return 0; > +} > + > /** > * main - main entry function of mkeficapsule > * @argc: Number of arguments > * @argv: Array of pointers to arguments > @@ -840,8 +979,15 @@ int main(int argc, char **argv) > char *privkey_file, *cert_file; > int c, idx; > struct fmp_payload_header_params fmp_ph_params = { 0 }; > > + /* Generate dynamic GUIDs */ > + if (argc > 1 && !strcmp(argv[1], "guidgen")) { > + if (genguid(argc - 1, argv + 1)) > + exit(EXIT_FAILURE); > + exit(EXIT_SUCCESS); > + } > + > guid = NULL; > index = 0; > instance = 0; > mcount = 0; > @@ -928,9 +1074,9 @@ int main(int argc, char **argv) > case 'D': > capsule_dump = true; > break; > default: > - print_usage(); > + print_usage_mkeficapsule(); > exit(EXIT_SUCCESS); > } > } > > @@ -951,9 +1097,9 @@ int main(int argc, char **argv) > (capsule_type != CAPSULE_NORMAL_BLOB && > ((argc != optind + 1) || > ((capsule_type == CAPSULE_ACCEPT) && !guid) || > ((capsule_type == CAPSULE_REVERT) && guid)))) { > - print_usage(); > + print_usage_mkeficapsule(); > exit(EXIT_FAILURE); > } > > if (capsule_type != CAPSULE_NORMAL_BLOB) { > > -- > 2.46.0 > Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1 index c4c2057d5c7a..bf735295effa 100644 --- a/doc/mkeficapsule.1 +++ b/doc/mkeficapsule.1 @@ -9,8 +9,11 @@ mkeficapsule \- Generate EFI capsule file for U-Boot .SH SYNOPSIS .B mkeficapsule .RI [ options ] " " [ image-blob ] " " capsule-file +.B mkeficapsule +.RI guidgen " " [ GUID ] " " DTB " " IMAGE_NAME... + .SH "DESCRIPTION" The .B mkeficapsule command is used to create an EFI capsule file to be used by U-Boot for firmware @@ -41,8 +44,12 @@ format is the same as used in the new uImage format and allows for multiple binary blobs in a single capsule file. This type of image file can be generated by .BR mkimage . +mkeficapsule can also be used to simulate the dynamic GUID generation used to +identify firmware images in capsule updates by providing the namespace guid, dtb +for the board, and a list of firmware images. + .SH "OPTIONS" .TP .BI "-g\fR,\fB --guid " guid-string @@ -112,8 +119,24 @@ at every firmware update. .TP .B "-d\fR,\fB --dump_sig" Dump signature data into *.p7 file +.SH "GUIDGEN OPTIONS" + +.TP +.B "[GUID]" +The namespace/salt GUID, by default this is EFI_CAPSULE_NAMESPACE_GUID. +The format is: + xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +.TP +.B DTB +The device tree blob file for the board. + +.TP +.B IMAGE_NAME... +The names of the firmware images to generate GUIDs for. + .PP .SH FILES .TP .I /EFI/UpdateCapsule diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 54fb4dee3ee5..8dfc09ffaf4f 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -19,12 +19,16 @@ #include <gnutls/gnutls.h> #include <gnutls/pkcs7.h> #include <gnutls/abstract.h> +#include <libfdt.h> #include <u-boot/uuid.h> #include "eficapsule.h" +// Matches CONFIG_EFI_CAPSULE_NAMESPACE_GUID +#define DEFAULT_NAMESPACE_GUID "8c9f137e-91dc-427b-b2d6-b420faebaf2a" + static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; @@ -53,11 +57,23 @@ static struct option options[] = { {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; -static void print_usage(void) + +static void print_usage_guidgen(void) { - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" + fprintf(stderr, "%s guidgen [GUID] DTB IMAGE_NAME...\n" + "Options:\n" + + "\tGUID Namespace GUID (default: %s)\n" + "\tDTB Device Tree Blob\n" + "\tIMAGE_NAME... One or more names of fw_images to generate GUIDs for\n", + tool_name, DEFAULT_NAMESPACE_GUID); +} + +static void print_usage_mkeficapsule(void) +{ + fprintf(stderr, "Usage: \n\n%s [options] <image blob> <output file>\n" "Options:\n" "\t-g, --guid <guid string> guid for image blob type\n" "\t-i, --index <index> update image index\n" @@ -70,10 +86,11 @@ static void print_usage(void) "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" "\t-D, --dump-capsule dump the contents of the capsule headers\n" - "\t-h, --help print a help message\n", + "\t-h, --help print a help message\n\n", tool_name); + print_usage_guidgen(); } /** * auth_context - authentication context @@ -816,8 +833,130 @@ static void dump_capsule_contents(char *capsule_file) exit(EXIT_FAILURE); } } +static struct fdt_header *load_dtb(const char *path) +{ + struct fdt_header *dtb; + ssize_t dtb_size; + FILE *f; + + /* Open and parse DTB */ + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Cannot open %s\n", path); + return NULL; + } + + if (fseek(f, 0, SEEK_END)) { + fprintf(stderr, "Cannot seek to the end of %s: %s\n", + path, strerror(errno)); + return NULL; + } + + dtb_size = ftell(f); + if (dtb_size < 0) { + fprintf(stderr, "Cannot ftell %s: %s\n", + path, strerror(errno)); + return NULL; + } + + fseek(f, 0, SEEK_SET); + + dtb = malloc(dtb_size); + if (!dtb) { + fprintf(stderr, "Can't allocated %ld\n", dtb_size); + return NULL; + } + + if (fread(dtb, dtb_size, 1, f) != 1) { + fprintf(stderr, "Can't read %ld bytes from %s\n", + dtb_size, path); + free(dtb); + return NULL; + } + + fclose(f); + + return dtb; +} + +#define MAX_IMAGE_NAME_LEN 128 +static int genguid(int argc, char **argv) +{ + int idx = 2, ret; + unsigned char namespace[16]; + struct efi_guid image_type_id; + const char *dtb_path; + struct fdt_header *dtb; + const char *compatible; + int compatlen, namelen; + uint16_t fw_image[MAX_IMAGE_NAME_LEN]; + + if (argc < 2) { + fprintf(stderr, "Usage: "); + print_usage_guidgen(); + return -1; + } + + if (uuid_str_to_bin(argv[1], namespace, UUID_STR_FORMAT_GUID)) { + uuid_str_to_bin(DEFAULT_NAMESPACE_GUID, namespace, UUID_STR_FORMAT_GUID); + dtb_path = argv[1]; + } else { + dtb_path = argv[2]; + idx = 3; + } + + if (idx == argc) { + fprintf(stderr, "Usage: "); + print_usage_guidgen(); + return -1; + } + + dtb = load_dtb(dtb_path); + if (!dtb) + return -1; + + if ((ret = fdt_check_header(dtb))) { + fprintf(stderr, "Invalid DTB header: %d\n", ret); + return -1; + } + + compatible = fdt_getprop(dtb, 0, "compatible", &compatlen); + if (!compatible) { + fprintf(stderr, "No compatible string found in DTB\n"); + return -1; + } + if (strnlen(compatible, compatlen) >= compatlen) { + fprintf(stderr, "Compatible string not null-terminated\n"); + return -1; + } + + printf("Generating GUIDs for %s with namespace %s:\n", + compatible, DEFAULT_NAMESPACE_GUID); + for (; idx < argc; idx++) { + memset(fw_image, 0, sizeof(fw_image)); + namelen = strlen(argv[idx]); + if (namelen > MAX_IMAGE_NAME_LEN) { + fprintf(stderr, "Image name too long: %s\n", argv[idx]); + return -1; + } + + for (int i = 0; i < namelen; i++) + fw_image[i] = (uint16_t)argv[idx][i]; + + gen_v5_guid((struct uuid *)&namespace, &image_type_id, + compatible, strlen(compatible), + fw_image, namelen * sizeof(uint16_t), + NULL); + + printf("%s: ", argv[idx]); + print_guid(&image_type_id); + } + + return 0; +} + /** * main - main entry function of mkeficapsule * @argc: Number of arguments * @argv: Array of pointers to arguments @@ -840,8 +979,15 @@ int main(int argc, char **argv) char *privkey_file, *cert_file; int c, idx; struct fmp_payload_header_params fmp_ph_params = { 0 }; + /* Generate dynamic GUIDs */ + if (argc > 1 && !strcmp(argv[1], "guidgen")) { + if (genguid(argc - 1, argv + 1)) + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); + } + guid = NULL; index = 0; instance = 0; mcount = 0; @@ -928,9 +1074,9 @@ int main(int argc, char **argv) case 'D': capsule_dump = true; break; default: - print_usage(); + print_usage_mkeficapsule(); exit(EXIT_SUCCESS); } } @@ -951,9 +1097,9 @@ int main(int argc, char **argv) (capsule_type != CAPSULE_NORMAL_BLOB && ((argc != optind + 1) || ((capsule_type == CAPSULE_ACCEPT) && !guid) || ((capsule_type == CAPSULE_REVERT) && guid)))) { - print_usage(); + print_usage_mkeficapsule(); exit(EXIT_FAILURE); } if (capsule_type != CAPSULE_NORMAL_BLOB) {
Add support for generating GUIDs that match those generated internally by U-Boot for capsule update fw_images when using dynamic UUIDs. Dynamic UUIDs in U-Boot work by taking a namespace UUID and hashing it with the board compatible and fw_image name. This feature just provides a way to determine the UUIDs for a particular board without having to actually boot U-Boot on it. Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org> --- doc/mkeficapsule.1 | 23 ++++++++ tools/mkeficapsule.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 174 insertions(+), 5 deletions(-)