[PATCH 0/5] Extend mkeficapsule tool to pack multiple payloads

From: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
Currently, the mkeficapsule tool supports at most one payload inside the capsule. However, the UEFI specification and the u-boot code support multiple payloads inside one capsule. Extend the tool by this feature. The tool is kept backwards-compatible, so it can still be used and called exactly as before if desired.
Malte Schmidt (5): mkeficapsule: constify function parameters mkeficapsule: add support for multiple payloads inside capsule test: efi_capsule: test a capsule update containing multiple images doc: uefi: update mkeficapsule documentation doc: uefi: clarify capsule concept
doc/develop/uefi/uefi.rst | 73 +- test/py/tests/test_efi_capsule/conftest.py | 18 +- .../test_capsule_firmware_raw.py | 46 +- .../test_capsule_firmware_signed_raw.py | 24 +- tools/eficapsule.h | 5 - tools/mkeficapsule.c | 651 +++++++++++++----- 6 files changed, 622 insertions(+), 195 deletions(-)

From: Malte Schmidt malte.schmidt@weidmueller.com
Use const keyword for function parameters where appropriate.
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com ---
tools/mkeficapsule.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 52be1f122e..b8db00b16b 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -88,8 +88,8 @@ static void print_usage(void) * are filled in by create_auth_data(). */ struct auth_context { - char *key_file; - char *cert_file; + const char *key_file; + const char *cert_file; uint8_t *image_data; size_t image_size; struct efi_firmware_image_authentication auth; @@ -112,7 +112,7 @@ static int dump_sig; * * 0 - on success * * -1 - on failure */ -static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size) +static int read_bin_file(const char *bin, uint8_t **data, off_t *bin_size) { FILE *g; struct stat bin_stat; @@ -170,7 +170,8 @@ err: * * 0 - on success * * -1 - on failure */ -static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg) +static int write_capsule_file(FILE *f, const void *data, size_t size, + const char *msg) { size_t size_written;
@@ -343,7 +344,8 @@ static int create_auth_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int dump_signature(const char *path, uint8_t *signature, size_t sig_size) +static int dump_signature(const char *path, const uint8_t *signature, + size_t sig_size) { char *sig_path; FILE *f; @@ -402,10 +404,12 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(char *path, char *bin, efi_guid_t *guid, - unsigned long index, unsigned long instance, - struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, char *privkey_file, char *cert_file, +static int create_fwbin(const char *path, const char *bin, + const efi_guid_t *guid, unsigned long index, + unsigned long instance, + const struct fmp_payload_header_params *fmp_ph_params, + uint64_t mcount, + const char *privkey_file, const char *cert_file, uint16_t oemflags) { struct efi_capsule_header header; @@ -604,7 +608,8 @@ void convert_uuid_to_guid(unsigned char *buf) buf[7] = c; }
-static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) +static int create_empty_capsule(const char *path, const efi_guid_t *guid, + bool fw_accept) { struct efi_capsule_header header = { 0 }; FILE *f = NULL; @@ -666,7 +671,7 @@ int main(int argc, char **argv) unsigned long index, instance; uint64_t mcount; unsigned long oemflags; - char *privkey_file, *cert_file; + const char *privkey_file, *cert_file; int c, idx; struct fmp_payload_header_params fmp_ph_params = { 0 };

On 6/16/23 13:34, Stefan Herbrechtsmeier wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
Thanks for considering which parameters may be constants.
nits:
The Urban Dictionary defines 'constify' as:
"To constantly do something, like constantly watching anime all day."
%s/constify function parameters/make function parameters const/
Use const keyword for function parameters where appropriate.
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/mkeficapsule.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 52be1f122e..b8db00b16b 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -88,8 +88,8 @@ static void print_usage(void)
- are filled in by create_auth_data().
*/ struct auth_context {
- char *key_file;
- char *cert_file;
- const char *key_file;
- const char *cert_file; uint8_t *image_data; size_t image_size; struct efi_firmware_image_authentication auth;
@@ -112,7 +112,7 @@ static int dump_sig;
- 0 - on success
- -1 - on failure
*/ -static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size) +static int read_bin_file(const char *bin, uint8_t **data, off_t *bin_size) { FILE *g; struct stat bin_stat; @@ -170,7 +170,8 @@ err:
- 0 - on success
- -1 - on failure
*/ -static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg) +static int write_capsule_file(FILE *f, const void *data, size_t size,
Why should size not be constant?
The name of the parameter 'size' does not match the function documentation which has 'bin_size'. Please, change either of both.
Parameter 'f' does not match the documentation which has 'bin'.
For each function that you touch, please, ensure that the function parameters are correctly documented.
{ size_t size_written;const char *msg)
@@ -343,7 +344,8 @@ static int create_auth_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int dump_signature(const char *path, uint8_t *signature, size_t sig_size) +static int dump_signature(const char *path, const uint8_t *signature,
size_t sig_size)
Why should sig_size not be constant?
{ char *sig_path; FILE *f; @@ -402,10 +404,12 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance,
struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount, char *privkey_file, char *cert_file,
+static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file, uint16_t oemflags)
Why shouldn't instance, mcount, oemflags be constant?
{ struct efi_capsule_header header; @@ -604,7 +608,8 @@ void convert_uuid_to_guid(unsigned char *buf) buf[7] = c; }
-static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) +static int create_empty_capsule(const char *path, const efi_guid_t *guid,
bool fw_accept)
Why should fw_accept not be constant?
Please, make the use of 'const' a bit more consistent.
Best regards
Heinrich
{ struct efi_capsule_header header = { 0 }; FILE *f = NULL; @@ -666,7 +671,7 @@ int main(int argc, char **argv) unsigned long index, instance; uint64_t mcount; unsigned long oemflags;
- char *privkey_file, *cert_file;
- const char *privkey_file, *cert_file; int c, idx; struct fmp_payload_header_params fmp_ph_params = { 0 };

Hello Heinrich,
thank you for you extensive review. I will incorporate your reviews in a future version of the patch series.
Best Regards Malte
Am 16.06.2023 um 20:18 schrieb Heinrich Schuchardt:
On 6/16/23 13:34, Stefan Herbrechtsmeier wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
Thanks for considering which parameters may be constants.
nits:
The Urban Dictionary defines 'constify' as:
"To constantly do something, like constantly watching anime all day."
%s/constify function parameters/make function parameters const/
Use const keyword for function parameters where appropriate.
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier
stefan.herbrechtsmeier@weidmueller.com
tools/mkeficapsule.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 52be1f122e..b8db00b16b 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -88,8 +88,8 @@ static void print_usage(void) * are filled in by create_auth_data(). */ struct auth_context { - char *key_file; - char *cert_file; + const char *key_file; + const char *cert_file; uint8_t *image_data; size_t image_size; struct efi_firmware_image_authentication auth; @@ -112,7 +112,7 @@ static int dump_sig; * * 0 - on success * * -1 - on failure */ -static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size) +static int read_bin_file(const char *bin, uint8_t **data, off_t *bin_size) { FILE *g; struct stat bin_stat; @@ -170,7 +170,8 @@ err: * * 0 - on success * * -1 - on failure */ -static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg) +static int write_capsule_file(FILE *f, const void *data, size_t size,
Why should size not be constant?
The name of the parameter 'size' does not match the function documentation which has 'bin_size'. Please, change either of both.
Parameter 'f' does not match the documentation which has 'bin'.
For each function that you touch, please, ensure that the function parameters are correctly documented.
+ const char *msg) { size_t size_written;
@@ -343,7 +344,8 @@ static int create_auth_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int dump_signature(const char *path, uint8_t *signature, size_t sig_size) +static int dump_signature(const char *path, const uint8_t *signature, + size_t sig_size)
Why should sig_size not be constant?
{ char *sig_path; FILE *f; @@ -402,10 +404,12 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(char *path, char *bin, efi_guid_t *guid, - unsigned long index, unsigned long instance, - struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, char *privkey_file, char *cert_file, +static int create_fwbin(const char *path, const char *bin, + const efi_guid_t *guid, unsigned long index, + unsigned long instance, + const struct fmp_payload_header_params *fmp_ph_params, + uint64_t mcount, + const char *privkey_file, const char *cert_file, uint16_t oemflags)
Why shouldn't instance, mcount, oemflags be constant?
{ struct efi_capsule_header header; @@ -604,7 +608,8 @@ void convert_uuid_to_guid(unsigned char *buf) buf[7] = c; }
-static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) +static int create_empty_capsule(const char *path, const efi_guid_t *guid, + bool fw_accept)
Why should fw_accept not be constant?
Please, make the use of 'const' a bit more consistent.
Best regards
Heinrich
{ struct efi_capsule_header header = { 0 }; FILE *f = NULL; @@ -666,7 +671,7 @@ int main(int argc, char **argv) unsigned long index, instance; uint64_t mcount; unsigned long oemflags; - char *privkey_file, *cert_file; + const char *privkey_file, *cert_file; int c, idx; struct fmp_payload_header_params fmp_ph_params = { 0 };

From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
[1] https://uefi.org/specs/UEFI/2.10/index.html
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com ---
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params { - bool have_header; - uint32_t fw_version; -}; - #endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'}, + {"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'}, @@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) { - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" + fprintf(stderr, "Usage: %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" - "\t-I, --instance <instance> update hardware instance\n" - "\t-v, --fw-version <version> firmware version\n" - "\t-p, --private-key <privkey file> private key file\n" - "\t-c, --certificate <cert file> signer's certificate file\n" - "\t-m, --monotonic-count <count> monotonic count\n" - "\t-d, --dump_sig dump signature (*.p7)\n" - "\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-h, --help print a help message\n", + "\t-g, --guid <guid list> comma-separated list of guids for image blob types\n" + "\t-i, --index <index list> comma-separated list of update image indices\n" + "\t-b, --image_blob <blob list> comma-separated list of image blobs\n" + "\t-I, --instance <instance list> comma-separated list of update hardware instances\n" + "\t-v, --fw-version <version list> comma-separated list of firmware versions\n" + "\t-p, --private-key <privkey file> private key file\n" + "\t-c, --certificate <cert file> signer's certificate file\n" + "\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n" + "\t-d, --dump_sig dump signature (*.p7)\n" + "\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-h, --help print a help message\n", tool_name); }
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx) * @path: Path to a capsule file * @signature: Signature data * @sig_size: Size of signature data + * @index: The payload index the signature belongs to * * Signature data pointed to by @signature will be saved into - * a file whose file name is @path with ".p7" suffix. + * a file whose file name is @path with "_<index>.p7" suffix. + * If index is negative the suffix is ".p7" (for backwards compatibility). * * Return: * * 0 - on success * * -1 - on failure */ static int dump_signature(const char *path, const uint8_t *signature, - size_t sig_size) + size_t sig_size, int index) { char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
- sprintf(sig_path, "%s.p7", path); + if (index < 0) + sprintf(sig_path, "%s.p7", path); + else + sprintf(sig_path, "%s_%d.p7", path, index); + f = fopen(sig_path, "w"); if (!f) goto err; @@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /** * create_fwbin - create an uefi capsule file * @path: Path to a created capsule file - * @bin: Path to a firmware binary to encapsulate - * @guid: GUID of related FMP driver - * @index: Index number in capsule + * @bins: Paths to firmware binaries to encapsulate, an array + * @guids: GUIDs of related FMP drivers, an array + * @indices: Index numbers in capsule, an array * @instance: Instance number in capsule * @mcount: Monotonic count in authentication information + * @size: Size of the arrays * @private_file: Path to a private key file * @cert_file: Path to a certificate file - * @oemflags: Capsule OEM Flags, bits 0-15 + * @oemflags: Capsule OEM Flags, bits 0-15 * * This function actually does the job of creating an uefi capsule file. * All the arguments must be supplied. @@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(const char *path, const char *bin, - const efi_guid_t *guid, unsigned long index, - unsigned long instance, - const struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, - const char *privkey_file, const char *cert_file, - uint16_t oemflags) +static int create_fwbin(const char *path, const char **bins, + const efi_guid_t *guids, const unsigned long *indices, + const unsigned long *instances, + const unsigned long *fw_versions, const unsigned long *mcounts, + int size, const char *privkey_file, + const char *cert_file, uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; - struct efi_firmware_management_capsule_image_header image; - struct auth_context auth_context; + struct efi_firmware_management_capsule_image_header images[size]; + struct auth_context auth_contexts[size]; FILE *f; - uint8_t *data, *new_data, *buf; - off_t bin_size; - uint64_t offset; + uint8_t *data_list[size], *new_data_list[size], *buf_list[size]; + off_t bin_sizes[size]; + uint64_t offsets[size]; int ret; - struct fmp_payload_header payload_header; + struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path); - fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid); - fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance); + for (int i = 0; i < size; i++) { + fprintf(stderr, "\tpayload no: %d\n", i); + fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]); + fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]); + } #endif - auth_context.sig_size = 0; f = NULL; - data = NULL; - new_data = NULL; ret = -1;
- /* - * read a firmware binary - */ - if (read_bin_file(bin, &data, &bin_size)) - goto err; + for (int i = 0; i < size; i++) { + auth_contexts[i].sig_size = 0; + data_list[i] = NULL; + new_data_list[i] = NULL; + }
- buf = data; + for (int i = 0; i < size; i++) { + int dump_index = (size == 1) ? -1 : i;
- /* insert fmp payload header right before the payload */ - if (fmp_ph_params->have_header) { - new_data = malloc(bin_size + sizeof(payload_header)); - if (!new_data) + /* + * read a firmware binary + */ + if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
- payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; - payload_header.header_size = sizeof(payload_header); - payload_header.fw_version = fmp_ph_params->fw_version; - payload_header.lowest_supported_version = 0; /* not used */ - memcpy(new_data, &payload_header, sizeof(payload_header)); - memcpy(new_data + sizeof(payload_header), data, bin_size); - buf = new_data; - bin_size += sizeof(payload_header); - } - - /* first, calculate signature to determine its size */ - if (privkey_file && cert_file) { - auth_context.key_file = privkey_file; - auth_context.cert_file = cert_file; - auth_context.auth.monotonic_count = mcount; - auth_context.image_data = buf; - auth_context.image_size = bin_size; - - if (create_auth_data(&auth_context)) { - fprintf(stderr, "Signing firmware image failed\n"); - goto err; + buf_list[i] = data_list[i]; + /* insert fmp payload header right before the payload */ + if (fw_versions) { + new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i])); + if (!new_data_list[i]) + goto err; + + payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE; + payload_headers[i].header_size = sizeof(payload_headers[i]); + payload_headers[i].fw_version = fw_versions[i]; + payload_headers[i].lowest_supported_version = 0; /* not used */ + memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i])); + memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i], + bin_sizes[i]); + buf_list[i] = new_data_list[i]; + bin_sizes[i] += sizeof(payload_headers[i]); }
- if (dump_sig && - dump_signature(path, auth_context.sig_data, - auth_context.sig_size)) { - fprintf(stderr, "Creating signature file failed\n"); - goto err; + /* calculate signature to determine its size */ + if (privkey_file && cert_file) { + auth_contexts[i].key_file = privkey_file; + auth_contexts[i].cert_file = cert_file; + auth_contexts[i].auth.monotonic_count = mcounts[i]; + auth_contexts[i].image_data = buf_list[i]; + auth_contexts[i].image_size = bin_sizes[i]; + + if (create_auth_data(&auth_contexts[i])) { + fprintf(stderr, "Signing firmware image failed\n"); + goto err; + } + + if (dump_sig && + dump_signature(path, auth_contexts[i].sig_data, + auth_contexts[i].sig_size, dump_index)) { + fprintf(stderr, "Creating signature file failed\n"); + goto err; + } } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header) - + sizeof(capsule) + sizeof(uint64_t) - + sizeof(image) - + bin_size; - if (auth_context.sig_size) - header.capsule_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; + + sizeof(capsule) + + size * sizeof(uint64_t); /* size of item_offset_list */ + for (int i = 0; i < size; i++) { + offsets[i] = header.capsule_image_size - sizeof(header); + header.capsule_image_size += sizeof(images[i]) + + bin_sizes[i]; + if (auth_contexts[i].sig_size) + header.capsule_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + } if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err;
/* * firmware capsule header - * This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0; - capsule.payload_item_count = 1; + capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
- offset = sizeof(capsule) + sizeof(uint64_t); - if (write_capsule_file(f, &offset, sizeof(offset), - "Offset to capsule image")) + if (write_capsule_file(f, &offsets, size * sizeof(uint64_t), + "Offsets to capsule images")) goto err;
- /* - * firmware capsule image header - */ - image.version = 0x00000003; - memcpy(&image.update_image_type_id, guid, sizeof(*guid)); - image.update_image_index = index; - image.reserved[0] = 0; - image.reserved[1] = 0; - image.reserved[2] = 0; - image.update_image_size = bin_size; - if (auth_context.sig_size) - image.update_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; - image.update_vendor_code_size = 0; /* none */ - image.update_hardware_instance = instance; - image.image_capsule_support = 0; - if (auth_context.sig_size) - image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; - if (write_capsule_file(f, &image, sizeof(image), - "Firmware capsule image header")) - goto err; - - /* - * signature - */ - if (auth_context.sig_size) { - if (write_capsule_file(f, &auth_context.auth, - sizeof(auth_context.auth), - "Authentication header")) + for (int i = 0; i < size; i++) { + /* + * firmware capsule image header + */ + images[i].version = 0x00000003; + memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i])); + images[i].update_image_index = indices[i]; + images[i].reserved[0] = 0; + images[i].reserved[1] = 0; + images[i].reserved[2] = 0; + images[i].update_image_size = bin_sizes[i]; + if (auth_contexts[i].sig_size) + images[i].update_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + images[i].update_vendor_code_size = 0; /* none */ + images[i].update_hardware_instance = instances[i]; + images[i].image_capsule_support = 0; + if (auth_contexts[i].sig_size) + images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; + if (write_capsule_file(f, &images[i], sizeof(images[i]), + "Firmware capsule image header")) goto err;
- if (write_capsule_file(f, auth_context.sig_data, - auth_context.sig_size, "Signature")) + /* + * signature + */ + if (auth_contexts[i].sig_size) { + if (write_capsule_file(f, &auth_contexts[i].auth, + sizeof(auth_contexts[i].auth), + "Authentication header")) + goto err; + + if (write_capsule_file(f, auth_contexts[i].sig_data, + auth_contexts[i].sig_size, "Signature")) + goto err; + } + + /* + * firmware binary + */ + if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
- /* - * firmware binary - */ - if (write_capsule_file(f, buf, bin_size, "Firmware binary")) - goto err; - ret = 0; err: if (f) fclose(f); - free_sig_data(&auth_context); - free(data); - free(new_data); + for (int i = 0; i < size; i++) { + free_sig_data(&auth_contexts[i]); + free(data_list[i]); + free(new_data_list[i]); + }
return ret; } @@ -652,6 +676,228 @@ err: return ret; }
+/** + * count_items - count number of items in list + * @list: Pointer to a string + * @separator: Separator used to separate list items + * + * Count the number of items in a list. The list items + * are separated by a separator character inside the string. + * Trailing white spaces are not allowed except if it is the separator. + * + * Return: + * The item count. + */ +int count_items(const char *list, char separator) +{ + const char *c; + int count = 0; + + if (!*list) + return 0; + + for (c = list; *c; c++) { + if (*c == separator) + count++; + } + /* correct count if no trailing separator present */ + if (*(c - 1) != separator) + count++; + + return count; +} + +/** + * update_itemcount - update item count + * @count: The count to be updated + * @list: The item list + * @separator: List separator + * + * Initialize the count if it is uninitialized (negative value). + * Check that the list contains at least one item. + * Check if an already initialized count is consistent with the list count. + * + * Return: + * * 0 - on success + * * -1 - if a check fails + */ +int update_itemcount(int *count, const char *list, char separator) +{ + int current_count = count_items(list, separator); + + if (*count < 0) + *count = current_count; + + if (*count == 0 || + *count != current_count) + return -1; + + return 0; +} + +/** + * split_list - split list into elements + * @elements: Pointer to string array + * @size: The array size + * @list: The item list + * @separator: List separator + * + * Split a comma-separated list into its elements. + * + * Return: + * * 0 - on success + * * -1 - on failure + */ +int split_list(char **elements, int size, char *list, char separator) +{ + const char separator_str[] = {separator, '\0'}; + char *end; + + for (int i = 0; i < size; i++) { + elements[i] = strsep(&list, separator_str); + if (!elements[i]) + return -1; + } + + end = strsep(&list, separator_str); /* NULL or empty string expected */ + if (end && *end) + return -1; + + return 0; +} + +/** + * alloc_array - allocate memory for array + * @count: The number of elements + * @obj_size: The size of a single element + * @name: The name of the array + * + * This is a wrapper for malloc which prints an error + * message on failure. + * + * Return: + * * Pointer to the allocated memory on success + * * NULL on failure + */ +void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{ + void *array; + + array = malloc(count * obj_size); + if (!array) + fprintf(stderr, "Could not allocate memory for %s\n", name); + + return array; +} + +/** + * init_guids - populate guid array + * @elements: String array of elements to be converted + * @size: The array size + * @name: The name of the array + * + * Allocate and populate an array of guid structs. The list contains the UUIDs + * to convert and store in the array. Upon failure an error message is + * printed. + * + * Return: + * * The initialized GUID array on success + * * NULL on failure + */ +efi_guid_t *init_guids(const char **elements, unsigned int size, + const char *name) +{ + efi_guid_t *guids; + + guids = alloc_array(size, sizeof(efi_guid_t), name); + if (!guids) + return NULL; + + for (int i = 0; i < size; i++) { + if (uuid_parse(elements[i], (unsigned char *)(guids + i))) { + fprintf(stderr, "Wrong %s format\n", name); + free(guids); + return NULL; + } + convert_uuid_to_guid((unsigned char *)(guids + i)); + } + + return guids; +} + +/** + * init_uls - populate unsigned long array + * @elements: String array of elements to be converted + * @size: The array size + * @name: The name of the array + * + * Allocate and populate an array of unsgined longs. Upon failure an + * error message is printed. + * + * Return: + * * The initialized array on success + * * NULL on failure + */ +unsigned long *init_uls(const char **elements, unsigned int size, + const char *name) +{ + unsigned long *array; + + array = alloc_array(size, sizeof(unsigned long), name); + if (!array) + return NULL; + for (int i = 0; i < size; i++) + array[i] = strtoul(elements[i], NULL, 0); + + return array; +} + +/** + * init_list - parse list and allocate elements + * @listcount: The list count to be checked and updated + * @list: The list to be parsed + * @separator: The list separator + * @name: The name of the list + * @multiple_times: List encountered multiple times + * + * Routine for command line argument lists. + * Parse the string list and count the list elements. + * Initialize the listcount if it is uninitialized (negative value). + * Check that the list contains at least one item. + * Check if an already initialized count is consistent with the list count. + * Allocate the string array and populate it with the list elements. + * The array should be freed in the calling function. + * Upon failure an error message is printed and the program exits. + * + * Return: + * * The initialized array on success + * * NULL on failure + */ +char **init_list(int *listcount, char *list, char separator, + bool multiple_times, char *name) +{ + char **elements; + + if (multiple_times) { + fprintf(stderr, "%s specified multiple times\n", name); + return NULL; + } + if (update_itemcount(listcount, list, separator)) { + fprintf(stderr, "List count not consistent with previous or list not provided\n"); + return NULL; + } + elements = alloc_array(*listcount, sizeof(char *), name); + if (!elements) + return NULL; + if (split_list(elements, *listcount, list, separator)) { + fprintf(stderr, "Could not parse %s list\n", name); + free(elements); + return NULL; + } + + return elements; +} + /** * main - main entry function of mkeficapsule * @argc: Number of arguments @@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) { - efi_guid_t *guid; - unsigned char uuid_buf[16]; - unsigned long index, instance; - uint64_t mcount; + const char separator = ','; + const efi_guid_t *guids; /* an array */ + const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags; + const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file; - int c, idx; - struct fmp_payload_header_params fmp_ph_params = { 0 }; + int listcount, c, idx;
- guid = NULL; - index = 0; - instance = 0; - mcount = 0; + guids = NULL; + indices = NULL; + instances = NULL; + mcounts = NULL; + oemflags = 0; + blob_paths = NULL; privkey_file = NULL; cert_file = NULL; + elements = NULL; + listcount = -1; + fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB; - oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g': - if (guid) { - fprintf(stderr, - "Image type already specified\n"); + elements = (const char **)init_list(&listcount, optarg, separator, !!guids, + "GUID"); + if (!elements) exit(EXIT_FAILURE); - } - if (uuid_parse(optarg, uuid_buf)) { - fprintf(stderr, "Wrong guid format\n"); + + guids = init_guids(elements, listcount, "GUID"); + if (!guids) exit(EXIT_FAILURE); - } - convert_uuid_to_guid(uuid_buf); - guid = (efi_guid_t *)uuid_buf; + + free(elements); + elements = NULL; break; case 'i': - index = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator, + !!indices, "index"); + if (!elements) + exit(EXIT_FAILURE); + + indices = init_uls(elements, listcount, "index"); + if (!indices) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; + break; + case 'b': + blob_paths = (const char **)init_list(&listcount, optarg, separator, + !!blob_paths, "blob path"); + if (!blob_paths) + exit(EXIT_FAILURE); break; case 'I': - instance = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator, + !!instances, "instance"); + if (!elements) + exit(EXIT_FAILURE); + + instances = init_uls(elements, listcount, "instance"); + if (!instances) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; break; case 'v': - fmp_ph_params.fw_version = strtoul(optarg, NULL, 0); - fmp_ph_params.have_header = true; + elements = (const char **)init_list(&listcount, optarg, separator, + !!fw_versions, "firmware version"); + if (!elements) + exit(EXIT_FAILURE); + + fw_versions = init_uls(elements, listcount, "firmware version"); + if (!fw_versions) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; break; case 'p': if (privkey_file) { @@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm': - mcount = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator, + !!mcounts, "monotonic count"); + if (!elements) + exit(EXIT_FAILURE); + + mcounts = init_uls(elements, listcount, "monotonic count"); + if (!mcounts) + exit(EXIT_FAILURE); + + free(elements); + elements = NULL; break; case 'd': dump_sig = 1; @@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB && - ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file)))) || + (!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + ((argc != optind + 1) || + ((capsule_type == CAPSULE_ACCEPT) && !guids) || + ((capsule_type == CAPSULE_ACCEPT) && listcount != 1) || + ((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
+ /* populate blob_paths if image blob was provided as positional argument */ + if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) { + blob_paths = malloc(sizeof(char *)); + if (!blob_paths) { + fprintf(stderr, "Could not allocate memory for blob paths\n"); + exit(EXIT_FAILURE); + } + *blob_paths = argv[argc - 2]; + } + + /* populate arrays with zeros if they are not provided */ + if (!indices) + indices = calloc(listcount, sizeof(unsigned long)); + if (!instances) + instances = calloc(listcount, sizeof(unsigned long)); + if (!mcounts) + mcounts = calloc(listcount, sizeof(uint64_t)); + if (capsule_type != CAPSULE_NORMAL_BLOB) { - if (create_empty_capsule(argv[argc - 1], guid, + if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } - } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, - index, instance, &fmp_ph_params, mcount, privkey_file, - cert_file, (uint16_t)oemflags) < 0) { + } else if (create_fwbin(argv[argc - 1], blob_paths, guids, + indices, instances, fw_versions, + mcounts, listcount, privkey_file, + cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }

hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params {
bool have_header;
uint32_t fw_version;
-};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'},
{"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'},
@@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
fprintf(stderr, "Usage: %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"
"\t-I, --instance <instance> update hardware instance\n"
"\t-v, --fw-version <version> firmware version\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n",
"\t-g, --guid <guid list> comma-separated list of guids for image blob types\n"
"\t-i, --index <index list> comma-separated list of update image indices\n"
"\t-b, --image_blob <blob list> comma-separated list of image blobs\n"
"\t-I, --instance <instance list> comma-separated list of update hardware instances\n"
"\t-v, --fw-version <version list> comma-separated list of firmware versions\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n", tool_name);
}
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx)
- @path: Path to a capsule file
- @signature: Signature data
- @sig_size: Size of signature data
- @index: The payload index the signature belongs to
- Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
*/
- If index is negative the suffix is ".p7" (for backwards compatibility).
- Return:
- 0 - on success
- -1 - on failure
static int dump_signature(const char *path, const uint8_t *signature,
size_t sig_size)
size_t sig_size, int index)
{ char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
sprintf(sig_path, "%s.p7", path);
if (index < 0)
sprintf(sig_path, "%s.p7", path);
else
sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err;
@@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /**
- create_fwbin - create an uefi capsule file
- @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
- @instance: Instance number in capsule
- @mcount: Monotonic count in authentication information
- @size: Size of the arrays
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file,
uint16_t oemflags)
+static int create_fwbin(const char *path, const char **bins,
const efi_guid_t *guids, const unsigned long *indices,
const unsigned long *instances,
const unsigned long *fw_versions, const unsigned long *mcounts,
int size, const char *privkey_file,
const char *cert_file, uint16_t oemflags)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule;
struct efi_firmware_management_capsule_image_header image;
struct auth_context auth_context;
struct efi_firmware_management_capsule_image_header images[size];
struct auth_context auth_contexts[size]; FILE *f;
uint8_t *data, *new_data, *buf;
off_t bin_size;
uint64_t offset;
uint8_t *data_list[size], *new_data_list[size], *buf_list[size];
off_t bin_sizes[size];
uint64_t offsets[size]; int ret;
struct fmp_payload_header payload_header;
struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path);
fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
for (int i = 0; i < size; i++) {
fprintf(stderr, "\tpayload no: %d\n", i);
fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]);
fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]);
}
#endif
auth_context.sig_size = 0; f = NULL;
data = NULL;
new_data = NULL; ret = -1;
/*
* read a firmware binary
*/
if (read_bin_file(bin, &data, &bin_size))
goto err;
for (int i = 0; i < size; i++) {
auth_contexts[i].sig_size = 0;
data_list[i] = NULL;
new_data_list[i] = NULL;
}
buf = data;
for (int i = 0; i < size; i++) {
int dump_index = (size == 1) ? -1 : i;
/* insert fmp payload header right before the payload */
if (fmp_ph_params->have_header) {
new_data = malloc(bin_size + sizeof(payload_header));
if (!new_data)
/*
* read a firmware binary
*/
if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_header.header_size = sizeof(payload_header);
payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */
memcpy(new_data, &payload_header, sizeof(payload_header));
memcpy(new_data + sizeof(payload_header), data, bin_size);
buf = new_data;
bin_size += sizeof(payload_header);
}
/* first, calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = buf;
auth_context.image_size = bin_size;
if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
buf_list[i] = data_list[i];
/* insert fmp payload header right before the payload */
if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i]));
if (!new_data_list[i])
goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i],
bin_sizes[i]);
buf_list[i] = new_data_list[i];
bin_sizes[i] += sizeof(payload_headers[i]); }
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
/* calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_contexts[i].key_file = privkey_file;
auth_contexts[i].cert_file = cert_file;
auth_contexts[i].auth.monotonic_count = mcounts[i];
auth_contexts[i].image_data = buf_list[i];
auth_contexts[i].image_size = bin_sizes[i];
if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
}
if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
} } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
+ sizeof(capsule) + sizeof(uint64_t)
+ sizeof(image)
+ bin_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
+ sizeof(capsule)
+ size * sizeof(uint64_t); /* size of item_offset_list */
for (int i = 0; i < size; i++) {
offsets[i] = header.capsule_image_size - sizeof(header);
header.capsule_image_size += sizeof(images[i])
+ bin_sizes[i];
if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
} if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err; /* * firmware capsule header
* This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0;
capsule.payload_item_count = 1;
capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
offset = sizeof(capsule) + sizeof(uint64_t);
if (write_capsule_file(f, &offset, sizeof(offset),
"Offset to capsule image"))
if (write_capsule_file(f, &offsets, size * sizeof(uint64_t),
"Offsets to capsule images")) goto err;
/*
* firmware capsule image header
*/
image.version = 0x00000003;
memcpy(&image.update_image_type_id, guid, sizeof(*guid));
image.update_image_index = index;
image.reserved[0] = 0;
image.reserved[1] = 0;
image.reserved[2] = 0;
image.update_image_size = bin_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */
image.update_hardware_instance = instance;
image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &image, sizeof(image),
"Firmware capsule image header"))
goto err;
/*
* signature
*/
if (auth_context.sig_size) {
if (write_capsule_file(f, &auth_context.auth,
sizeof(auth_context.auth),
"Authentication header"))
for (int i = 0; i < size; i++) {
/*
* firmware capsule image header
*/
images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i]));
images[i].update_image_index = indices[i];
images[i].reserved[0] = 0;
images[i].reserved[1] = 0;
images[i].reserved[2] = 0;
images[i].update_image_size = bin_sizes[i];
if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
images[i].update_vendor_code_size = 0; /* none */
images[i].update_hardware_instance = instances[i];
images[i].image_capsule_support = 0;
if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
if (write_capsule_file(f, auth_context.sig_data,
auth_context.sig_size, "Signature"))
/*
* signature
*/
if (auth_contexts[i].sig_size) {
if (write_capsule_file(f, &auth_contexts[i].auth,
sizeof(auth_contexts[i].auth),
"Authentication header"))
goto err;
if (write_capsule_file(f, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, "Signature"))
goto err;
}
/*
* firmware binary
*/
if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
/*
* firmware binary
*/
if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
goto err;
ret = 0;
err: if (f) fclose(f);
free_sig_data(&auth_context);
free(data);
free(new_data);
for (int i = 0; i < size; i++) {
free_sig_data(&auth_contexts[i]);
free(data_list[i]);
free(new_data_list[i]);
} return ret;
} @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{
const char *c;
int count = 0;
if (!*list)
return 0;
for (c = list; *c; c++) {
if (*c == separator)
count++;
}
/* correct count if no trailing separator present */
if (*(c - 1) != separator)
count++;
return count;
+}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{
int current_count = count_items(list, separator);
if (*count < 0)
*count = current_count;
if (*count == 0 ||
*count != current_count)
return -1;
return 0;
+}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{
const char separator_str[] = {separator, '\0'};
char *end;
for (int i = 0; i < size; i++) {
elements[i] = strsep(&list, separator_str);
if (!elements[i])
return -1;
}
end = strsep(&list, separator_str); /* NULL or empty string expected */
if (end && *end)
return -1;
return 0;
+}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{
void *array;
array = malloc(count * obj_size);
if (!array)
fprintf(stderr, "Could not allocate memory for %s\n", name);
return array;
+}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list contains the UUIDs
- to convert and store in the array. Upon failure an error message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size,
const char *name)
+{
efi_guid_t *guids;
guids = alloc_array(size, sizeof(efi_guid_t), name);
if (!guids)
return NULL;
for (int i = 0; i < size; i++) {
if (uuid_parse(elements[i], (unsigned char *)(guids + i))) {
fprintf(stderr, "Wrong %s format\n", name);
free(guids);
return NULL;
}
convert_uuid_to_guid((unsigned char *)(guids + i));
}
return guids;
+}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size,
const char *name)
+{
unsigned long *array;
array = alloc_array(size, sizeof(unsigned long), name);
if (!array)
return NULL;
for (int i = 0; i < size; i++)
array[i] = strtoul(elements[i], NULL, 0);
return array;
+}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator,
bool multiple_times, char *name)
+{
char **elements;
if (multiple_times) {
fprintf(stderr, "%s specified multiple times\n", name);
return NULL;
}
if (update_itemcount(listcount, list, separator)) {
fprintf(stderr, "List count not consistent with previous or list not provided\n");
return NULL;
}
elements = alloc_array(*listcount, sizeof(char *), name);
if (!elements)
return NULL;
if (split_list(elements, *listcount, list, separator)) {
fprintf(stderr, "Could not parse %s list\n", name);
free(elements);
return NULL;
}
return elements;
+}
/**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) {
efi_guid_t *guid;
unsigned char uuid_buf[16];
unsigned long index, instance;
uint64_t mcount;
const char separator = ',';
const efi_guid_t *guids; /* an array */
const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags;
const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file;
int c, idx;
struct fmp_payload_header_params fmp_ph_params = { 0 };
int listcount, c, idx;
guid = NULL;
index = 0;
instance = 0;
mcount = 0;
guids = NULL;
indices = NULL;
instances = NULL;
mcounts = NULL;
oemflags = 0;
blob_paths = NULL; privkey_file = NULL; cert_file = NULL;
elements = NULL;
listcount = -1;
fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g':
if (guid) {
fprintf(stderr,
"Image type already specified\n");
elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
"GUID");
if (!elements) exit(EXIT_FAILURE);
}
if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
guids = init_guids(elements, listcount, "GUID");
if (!guids) exit(EXIT_FAILURE);
}
convert_uuid_to_guid(uuid_buf);
guid = (efi_guid_t *)uuid_buf;
free(elements);
elements = NULL; break; case 'i':
index = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!indices, "index");
if (!elements)
exit(EXIT_FAILURE);
indices = init_uls(elements, listcount, "index");
if (!indices)
exit(EXIT_FAILURE);
free(elements);
elements = NULL;
break;
case 'b':
blob_paths = (const char **)init_list(&listcount, optarg, separator,
!!blob_paths, "blob path");
if (!blob_paths)
exit(EXIT_FAILURE); break; case 'I':
instance = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!instances, "instance");
if (!elements)
exit(EXIT_FAILURE);
instances = init_uls(elements, listcount, "instance");
if (!instances)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'v':
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
fmp_ph_params.have_header = true;
elements = (const char **)init_list(&listcount, optarg, separator,
!!fw_versions, "firmware version");
if (!elements)
exit(EXIT_FAILURE);
fw_versions = init_uls(elements, listcount, "firmware version");
if (!fw_versions)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'p': if (privkey_file) {
@@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm':
mcount = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!mcounts, "monotonic count");
if (!elements)
exit(EXIT_FAILURE);
mcounts = init_uls(elements, listcount, "monotonic count");
if (!mcounts)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'd': dump_sig = 1;
@@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB &&
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guid) ||
((capsule_type == CAPSULE_REVERT) && guid)))) {
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guids) ||
((capsule_type == CAPSULE_ACCEPT) && listcount != 1) ||
((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
/* populate blob_paths if image blob was provided as positional argument */
if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) {
blob_paths = malloc(sizeof(char *));
if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n");
exit(EXIT_FAILURE);
}
*blob_paths = argv[argc - 2];
}
/* populate arrays with zeros if they are not provided */
if (!indices)
indices = calloc(listcount, sizeof(unsigned long));
if (!instances)
instances = calloc(listcount, sizeof(unsigned long));
if (!mcounts)
mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) {
if (create_empty_capsule(argv[argc - 1], guid,
if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); }
} else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) {
} else if (create_fwbin(argv[argc - 1], blob_paths, guids,
indices, instances, fw_versions,
mcounts, listcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.30.2

On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
-sughosh
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params {
bool have_header;
uint32_t fw_version;
-};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'},
{"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'},
@@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
fprintf(stderr, "Usage: %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"
"\t-I, --instance <instance> update hardware instance\n"
"\t-v, --fw-version <version> firmware version\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n",
"\t-g, --guid <guid list> comma-separated list of guids for image blob types\n"
"\t-i, --index <index list> comma-separated list of update image indices\n"
"\t-b, --image_blob <blob list> comma-separated list of image blobs\n"
"\t-I, --instance <instance list> comma-separated list of update hardware instances\n"
"\t-v, --fw-version <version list> comma-separated list of firmware versions\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n", tool_name);
}
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx)
- @path: Path to a capsule file
- @signature: Signature data
- @sig_size: Size of signature data
- @index: The payload index the signature belongs to
- Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
*/
- If index is negative the suffix is ".p7" (for backwards compatibility).
- Return:
- 0 - on success
- -1 - on failure
static int dump_signature(const char *path, const uint8_t *signature,
size_t sig_size)
size_t sig_size, int index)
{ char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
sprintf(sig_path, "%s.p7", path);
if (index < 0)
sprintf(sig_path, "%s.p7", path);
else
sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err;
@@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /**
- create_fwbin - create an uefi capsule file
- @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
- @instance: Instance number in capsule
- @mcount: Monotonic count in authentication information
- @size: Size of the arrays
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file,
uint16_t oemflags)
+static int create_fwbin(const char *path, const char **bins,
const efi_guid_t *guids, const unsigned long *indices,
const unsigned long *instances,
const unsigned long *fw_versions, const unsigned long *mcounts,
int size, const char *privkey_file,
const char *cert_file, uint16_t oemflags)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule;
struct efi_firmware_management_capsule_image_header image;
struct auth_context auth_context;
struct efi_firmware_management_capsule_image_header images[size];
struct auth_context auth_contexts[size]; FILE *f;
uint8_t *data, *new_data, *buf;
off_t bin_size;
uint64_t offset;
uint8_t *data_list[size], *new_data_list[size], *buf_list[size];
off_t bin_sizes[size];
uint64_t offsets[size]; int ret;
struct fmp_payload_header payload_header;
struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path);
fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
for (int i = 0; i < size; i++) {
fprintf(stderr, "\tpayload no: %d\n", i);
fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]);
fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]);
}
#endif
auth_context.sig_size = 0; f = NULL;
data = NULL;
new_data = NULL; ret = -1;
/*
* read a firmware binary
*/
if (read_bin_file(bin, &data, &bin_size))
goto err;
for (int i = 0; i < size; i++) {
auth_contexts[i].sig_size = 0;
data_list[i] = NULL;
new_data_list[i] = NULL;
}
buf = data;
for (int i = 0; i < size; i++) {
int dump_index = (size == 1) ? -1 : i;
/* insert fmp payload header right before the payload */
if (fmp_ph_params->have_header) {
new_data = malloc(bin_size + sizeof(payload_header));
if (!new_data)
/*
* read a firmware binary
*/
if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_header.header_size = sizeof(payload_header);
payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */
memcpy(new_data, &payload_header, sizeof(payload_header));
memcpy(new_data + sizeof(payload_header), data, bin_size);
buf = new_data;
bin_size += sizeof(payload_header);
}
/* first, calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = buf;
auth_context.image_size = bin_size;
if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
buf_list[i] = data_list[i];
/* insert fmp payload header right before the payload */
if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i]));
if (!new_data_list[i])
goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i],
bin_sizes[i]);
buf_list[i] = new_data_list[i];
bin_sizes[i] += sizeof(payload_headers[i]); }
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
/* calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_contexts[i].key_file = privkey_file;
auth_contexts[i].cert_file = cert_file;
auth_contexts[i].auth.monotonic_count = mcounts[i];
auth_contexts[i].image_data = buf_list[i];
auth_contexts[i].image_size = bin_sizes[i];
if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
}
if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
} } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
+ sizeof(capsule) + sizeof(uint64_t)
+ sizeof(image)
+ bin_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
+ sizeof(capsule)
+ size * sizeof(uint64_t); /* size of item_offset_list */
for (int i = 0; i < size; i++) {
offsets[i] = header.capsule_image_size - sizeof(header);
header.capsule_image_size += sizeof(images[i])
+ bin_sizes[i];
if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
} if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err; /* * firmware capsule header
* This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0;
capsule.payload_item_count = 1;
capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
offset = sizeof(capsule) + sizeof(uint64_t);
if (write_capsule_file(f, &offset, sizeof(offset),
"Offset to capsule image"))
if (write_capsule_file(f, &offsets, size * sizeof(uint64_t),
"Offsets to capsule images")) goto err;
/*
* firmware capsule image header
*/
image.version = 0x00000003;
memcpy(&image.update_image_type_id, guid, sizeof(*guid));
image.update_image_index = index;
image.reserved[0] = 0;
image.reserved[1] = 0;
image.reserved[2] = 0;
image.update_image_size = bin_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */
image.update_hardware_instance = instance;
image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &image, sizeof(image),
"Firmware capsule image header"))
goto err;
/*
* signature
*/
if (auth_context.sig_size) {
if (write_capsule_file(f, &auth_context.auth,
sizeof(auth_context.auth),
"Authentication header"))
for (int i = 0; i < size; i++) {
/*
* firmware capsule image header
*/
images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i]));
images[i].update_image_index = indices[i];
images[i].reserved[0] = 0;
images[i].reserved[1] = 0;
images[i].reserved[2] = 0;
images[i].update_image_size = bin_sizes[i];
if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
images[i].update_vendor_code_size = 0; /* none */
images[i].update_hardware_instance = instances[i];
images[i].image_capsule_support = 0;
if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
if (write_capsule_file(f, auth_context.sig_data,
auth_context.sig_size, "Signature"))
/*
* signature
*/
if (auth_contexts[i].sig_size) {
if (write_capsule_file(f, &auth_contexts[i].auth,
sizeof(auth_contexts[i].auth),
"Authentication header"))
goto err;
if (write_capsule_file(f, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, "Signature"))
goto err;
}
/*
* firmware binary
*/
if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
/*
* firmware binary
*/
if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
goto err;
ret = 0;
err: if (f) fclose(f);
free_sig_data(&auth_context);
free(data);
free(new_data);
for (int i = 0; i < size; i++) {
free_sig_data(&auth_contexts[i]);
free(data_list[i]);
free(new_data_list[i]);
} return ret;
} @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{
const char *c;
int count = 0;
if (!*list)
return 0;
for (c = list; *c; c++) {
if (*c == separator)
count++;
}
/* correct count if no trailing separator present */
if (*(c - 1) != separator)
count++;
return count;
+}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{
int current_count = count_items(list, separator);
if (*count < 0)
*count = current_count;
if (*count == 0 ||
*count != current_count)
return -1;
return 0;
+}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{
const char separator_str[] = {separator, '\0'};
char *end;
for (int i = 0; i < size; i++) {
elements[i] = strsep(&list, separator_str);
if (!elements[i])
return -1;
}
end = strsep(&list, separator_str); /* NULL or empty string expected */
if (end && *end)
return -1;
return 0;
+}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{
void *array;
array = malloc(count * obj_size);
if (!array)
fprintf(stderr, "Could not allocate memory for %s\n", name);
return array;
+}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list contains the UUIDs
- to convert and store in the array. Upon failure an error message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size,
const char *name)
+{
efi_guid_t *guids;
guids = alloc_array(size, sizeof(efi_guid_t), name);
if (!guids)
return NULL;
for (int i = 0; i < size; i++) {
if (uuid_parse(elements[i], (unsigned char *)(guids + i))) {
fprintf(stderr, "Wrong %s format\n", name);
free(guids);
return NULL;
}
convert_uuid_to_guid((unsigned char *)(guids + i));
}
return guids;
+}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size,
const char *name)
+{
unsigned long *array;
array = alloc_array(size, sizeof(unsigned long), name);
if (!array)
return NULL;
for (int i = 0; i < size; i++)
array[i] = strtoul(elements[i], NULL, 0);
return array;
+}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator,
bool multiple_times, char *name)
+{
char **elements;
if (multiple_times) {
fprintf(stderr, "%s specified multiple times\n", name);
return NULL;
}
if (update_itemcount(listcount, list, separator)) {
fprintf(stderr, "List count not consistent with previous or list not provided\n");
return NULL;
}
elements = alloc_array(*listcount, sizeof(char *), name);
if (!elements)
return NULL;
if (split_list(elements, *listcount, list, separator)) {
fprintf(stderr, "Could not parse %s list\n", name);
free(elements);
return NULL;
}
return elements;
+}
/**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) {
efi_guid_t *guid;
unsigned char uuid_buf[16];
unsigned long index, instance;
uint64_t mcount;
const char separator = ',';
const efi_guid_t *guids; /* an array */
const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags;
const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file;
int c, idx;
struct fmp_payload_header_params fmp_ph_params = { 0 };
int listcount, c, idx;
guid = NULL;
index = 0;
instance = 0;
mcount = 0;
guids = NULL;
indices = NULL;
instances = NULL;
mcounts = NULL;
oemflags = 0;
blob_paths = NULL; privkey_file = NULL; cert_file = NULL;
elements = NULL;
listcount = -1;
fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g':
if (guid) {
fprintf(stderr,
"Image type already specified\n");
elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
"GUID");
if (!elements) exit(EXIT_FAILURE);
}
if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
guids = init_guids(elements, listcount, "GUID");
if (!guids) exit(EXIT_FAILURE);
}
convert_uuid_to_guid(uuid_buf);
guid = (efi_guid_t *)uuid_buf;
free(elements);
elements = NULL; break; case 'i':
index = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!indices, "index");
if (!elements)
exit(EXIT_FAILURE);
indices = init_uls(elements, listcount, "index");
if (!indices)
exit(EXIT_FAILURE);
free(elements);
elements = NULL;
break;
case 'b':
blob_paths = (const char **)init_list(&listcount, optarg, separator,
!!blob_paths, "blob path");
if (!blob_paths)
exit(EXIT_FAILURE); break; case 'I':
instance = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!instances, "instance");
if (!elements)
exit(EXIT_FAILURE);
instances = init_uls(elements, listcount, "instance");
if (!instances)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'v':
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
fmp_ph_params.have_header = true;
elements = (const char **)init_list(&listcount, optarg, separator,
!!fw_versions, "firmware version");
if (!elements)
exit(EXIT_FAILURE);
fw_versions = init_uls(elements, listcount, "firmware version");
if (!fw_versions)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'p': if (privkey_file) {
@@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm':
mcount = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!mcounts, "monotonic count");
if (!elements)
exit(EXIT_FAILURE);
mcounts = init_uls(elements, listcount, "monotonic count");
if (!mcounts)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'd': dump_sig = 1;
@@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB &&
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guid) ||
((capsule_type == CAPSULE_REVERT) && guid)))) {
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guids) ||
((capsule_type == CAPSULE_ACCEPT) && listcount != 1) ||
((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
/* populate blob_paths if image blob was provided as positional argument */
if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) {
blob_paths = malloc(sizeof(char *));
if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n");
exit(EXIT_FAILURE);
}
*blob_paths = argv[argc - 2];
}
/* populate arrays with zeros if they are not provided */
if (!indices)
indices = calloc(listcount, sizeof(unsigned long));
if (!instances)
instances = calloc(listcount, sizeof(unsigned long));
if (!mcounts)
mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) {
if (create_empty_capsule(argv[argc - 1], guid,
if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); }
} else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) {
} else if (create_fwbin(argv[argc - 1], blob_paths, guids,
indices, instances, fw_versions,
mcounts, listcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.30.2

Hi Sughos,
Am 16.06.2023 um 14:32 schrieb Sughosh Ganu:
On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
-sughosh
thanks for the heads up. So your opinion is that we only support multiple payloads via config files and not the command line? I think it does not hurt to have both options available.
I plan to rebase my code on yours once it nears the finish line. I still have a suggsetion for it which I will post in a sec.
Best Regards Malte
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params {
bool have_header;
uint32_t fw_version;
-};
- #endif /* _EFI_CAPSULE_H */
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'},
{"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'},
@@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
fprintf(stderr, "Usage: %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"
"\t-I, --instance <instance> update hardware instance\n"
"\t-v, --fw-version <version> firmware version\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n",
"\t-g, --guid <guid list> comma-separated list of guids for image blob types\n"
"\t-i, --index <index list> comma-separated list of update image indices\n"
"\t-b, --image_blob <blob list> comma-separated list of image blobs\n"
"\t-I, --instance <instance list> comma-separated list of update hardware instances\n"
"\t-v, --fw-version <version list> comma-separated list of firmware versions\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n", tool_name);
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx)
- @path: Path to a capsule file
- @signature: Signature data
- @sig_size: Size of signature data
- @index: The payload index the signature belongs to
- Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
*/ static int dump_signature(const char *path, const uint8_t *signature,
- If index is negative the suffix is ".p7" (for backwards compatibility).
- Return:
- 0 - on success
- -1 - on failure
size_t sig_size)
{ char *sig_path; FILE *f;size_t sig_size, int index)
@@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
sprintf(sig_path, "%s.p7", path);
if (index < 0)
sprintf(sig_path, "%s.p7", path);
else
sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err;
@@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /**
- create_fwbin - create an uefi capsule file
- @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
- @instance: Instance number in capsule
- @mcount: Monotonic count in authentication information
- @size: Size of the arrays
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file,
uint16_t oemflags)
+static int create_fwbin(const char *path, const char **bins,
const efi_guid_t *guids, const unsigned long *indices,
const unsigned long *instances,
const unsigned long *fw_versions, const unsigned long *mcounts,
int size, const char *privkey_file,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule;const char *cert_file, uint16_t oemflags)
struct efi_firmware_management_capsule_image_header image;
struct auth_context auth_context;
struct efi_firmware_management_capsule_image_header images[size];
struct auth_context auth_contexts[size]; FILE *f;
uint8_t *data, *new_data, *buf;
off_t bin_size;
uint64_t offset;
uint8_t *data_list[size], *new_data_list[size], *buf_list[size];
off_t bin_sizes[size];
uint64_t offsets[size]; int ret;
struct fmp_payload_header payload_header;
struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path);
fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
for (int i = 0; i < size; i++) {
fprintf(stderr, "\tpayload no: %d\n", i);
fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]);
fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]);
#endif}
auth_context.sig_size = 0; f = NULL;
data = NULL;
new_data = NULL; ret = -1;
/*
* read a firmware binary
*/
if (read_bin_file(bin, &data, &bin_size))
goto err;
for (int i = 0; i < size; i++) {
auth_contexts[i].sig_size = 0;
data_list[i] = NULL;
new_data_list[i] = NULL;
}
buf = data;
for (int i = 0; i < size; i++) {
int dump_index = (size == 1) ? -1 : i;
/* insert fmp payload header right before the payload */
if (fmp_ph_params->have_header) {
new_data = malloc(bin_size + sizeof(payload_header));
if (!new_data)
/*
* read a firmware binary
*/
if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_header.header_size = sizeof(payload_header);
payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */
memcpy(new_data, &payload_header, sizeof(payload_header));
memcpy(new_data + sizeof(payload_header), data, bin_size);
buf = new_data;
bin_size += sizeof(payload_header);
}
/* first, calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = buf;
auth_context.image_size = bin_size;
if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
buf_list[i] = data_list[i];
/* insert fmp payload header right before the payload */
if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i]));
if (!new_data_list[i])
goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i],
bin_sizes[i]);
buf_list[i] = new_data_list[i];
bin_sizes[i] += sizeof(payload_headers[i]); }
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
/* calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_contexts[i].key_file = privkey_file;
auth_contexts[i].cert_file = cert_file;
auth_contexts[i].auth.monotonic_count = mcounts[i];
auth_contexts[i].image_data = buf_list[i];
auth_contexts[i].image_size = bin_sizes[i];
if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
}
if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
} } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
+ sizeof(capsule) + sizeof(uint64_t)
+ sizeof(image)
+ bin_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
+ sizeof(capsule)
+ size * sizeof(uint64_t); /* size of item_offset_list */
for (int i = 0; i < size; i++) {
offsets[i] = header.capsule_image_size - sizeof(header);
header.capsule_image_size += sizeof(images[i])
+ bin_sizes[i];
if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
} if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err; /* * firmware capsule header
* This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0;
capsule.payload_item_count = 1;
capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
offset = sizeof(capsule) + sizeof(uint64_t);
if (write_capsule_file(f, &offset, sizeof(offset),
"Offset to capsule image"))
if (write_capsule_file(f, &offsets, size * sizeof(uint64_t),
"Offsets to capsule images")) goto err;
/*
* firmware capsule image header
*/
image.version = 0x00000003;
memcpy(&image.update_image_type_id, guid, sizeof(*guid));
image.update_image_index = index;
image.reserved[0] = 0;
image.reserved[1] = 0;
image.reserved[2] = 0;
image.update_image_size = bin_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */
image.update_hardware_instance = instance;
image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &image, sizeof(image),
"Firmware capsule image header"))
goto err;
/*
* signature
*/
if (auth_context.sig_size) {
if (write_capsule_file(f, &auth_context.auth,
sizeof(auth_context.auth),
"Authentication header"))
for (int i = 0; i < size; i++) {
/*
* firmware capsule image header
*/
images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i]));
images[i].update_image_index = indices[i];
images[i].reserved[0] = 0;
images[i].reserved[1] = 0;
images[i].reserved[2] = 0;
images[i].update_image_size = bin_sizes[i];
if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
images[i].update_vendor_code_size = 0; /* none */
images[i].update_hardware_instance = instances[i];
images[i].image_capsule_support = 0;
if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
if (write_capsule_file(f, auth_context.sig_data,
auth_context.sig_size, "Signature"))
/*
* signature
*/
if (auth_contexts[i].sig_size) {
if (write_capsule_file(f, &auth_contexts[i].auth,
sizeof(auth_contexts[i].auth),
"Authentication header"))
goto err;
if (write_capsule_file(f, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, "Signature"))
goto err;
}
/*
* firmware binary
*/
if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
/*
* firmware binary
*/
if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
goto err;
err: if (f) fclose(f);ret = 0;
free_sig_data(&auth_context);
free(data);
free(new_data);
for (int i = 0; i < size; i++) {
free_sig_data(&auth_contexts[i]);
free(data_list[i]);
free(new_data_list[i]);
} return ret;
}
@@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{
const char *c;
int count = 0;
if (!*list)
return 0;
for (c = list; *c; c++) {
if (*c == separator)
count++;
}
/* correct count if no trailing separator present */
if (*(c - 1) != separator)
count++;
return count;
+}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{
int current_count = count_items(list, separator);
if (*count < 0)
*count = current_count;
if (*count == 0 ||
*count != current_count)
return -1;
return 0;
+}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{
const char separator_str[] = {separator, '\0'};
char *end;
for (int i = 0; i < size; i++) {
elements[i] = strsep(&list, separator_str);
if (!elements[i])
return -1;
}
end = strsep(&list, separator_str); /* NULL or empty string expected */
if (end && *end)
return -1;
return 0;
+}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{
void *array;
array = malloc(count * obj_size);
if (!array)
fprintf(stderr, "Could not allocate memory for %s\n", name);
return array;
+}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list contains the UUIDs
- to convert and store in the array. Upon failure an error message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size,
const char *name)
+{
efi_guid_t *guids;
guids = alloc_array(size, sizeof(efi_guid_t), name);
if (!guids)
return NULL;
for (int i = 0; i < size; i++) {
if (uuid_parse(elements[i], (unsigned char *)(guids + i))) {
fprintf(stderr, "Wrong %s format\n", name);
free(guids);
return NULL;
}
convert_uuid_to_guid((unsigned char *)(guids + i));
}
return guids;
+}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size,
const char *name)
+{
unsigned long *array;
array = alloc_array(size, sizeof(unsigned long), name);
if (!array)
return NULL;
for (int i = 0; i < size; i++)
array[i] = strtoul(elements[i], NULL, 0);
return array;
+}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator,
bool multiple_times, char *name)
+{
char **elements;
if (multiple_times) {
fprintf(stderr, "%s specified multiple times\n", name);
return NULL;
}
if (update_itemcount(listcount, list, separator)) {
fprintf(stderr, "List count not consistent with previous or list not provided\n");
return NULL;
}
elements = alloc_array(*listcount, sizeof(char *), name);
if (!elements)
return NULL;
if (split_list(elements, *listcount, list, separator)) {
fprintf(stderr, "Could not parse %s list\n", name);
free(elements);
return NULL;
}
return elements;
+}
- /**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) {
efi_guid_t *guid;
unsigned char uuid_buf[16];
unsigned long index, instance;
uint64_t mcount;
const char separator = ',';
const efi_guid_t *guids; /* an array */
const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags;
const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file;
int c, idx;
struct fmp_payload_header_params fmp_ph_params = { 0 };
int listcount, c, idx;
guid = NULL;
index = 0;
instance = 0;
mcount = 0;
guids = NULL;
indices = NULL;
instances = NULL;
mcounts = NULL;
oemflags = 0;
blob_paths = NULL; privkey_file = NULL; cert_file = NULL;
elements = NULL;
listcount = -1;
fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g':
if (guid) {
fprintf(stderr,
"Image type already specified\n");
elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
"GUID");
if (!elements) exit(EXIT_FAILURE);
}
if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
guids = init_guids(elements, listcount, "GUID");
if (!guids) exit(EXIT_FAILURE);
}
convert_uuid_to_guid(uuid_buf);
guid = (efi_guid_t *)uuid_buf;
free(elements);
elements = NULL; break; case 'i':
index = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!indices, "index");
if (!elements)
exit(EXIT_FAILURE);
indices = init_uls(elements, listcount, "index");
if (!indices)
exit(EXIT_FAILURE);
free(elements);
elements = NULL;
break;
case 'b':
blob_paths = (const char **)init_list(&listcount, optarg, separator,
!!blob_paths, "blob path");
if (!blob_paths)
exit(EXIT_FAILURE); break; case 'I':
instance = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!instances, "instance");
if (!elements)
exit(EXIT_FAILURE);
instances = init_uls(elements, listcount, "instance");
if (!instances)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'v':
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
fmp_ph_params.have_header = true;
elements = (const char **)init_list(&listcount, optarg, separator,
!!fw_versions, "firmware version");
if (!elements)
exit(EXIT_FAILURE);
fw_versions = init_uls(elements, listcount, "firmware version");
if (!fw_versions)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'p': if (privkey_file) {
@@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm':
mcount = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!mcounts, "monotonic count");
if (!elements)
exit(EXIT_FAILURE);
mcounts = init_uls(elements, listcount, "monotonic count");
if (!mcounts)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'd': dump_sig = 1;
@@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB &&
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guid) ||
((capsule_type == CAPSULE_REVERT) && guid)))) {
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guids) ||
((capsule_type == CAPSULE_ACCEPT) && listcount != 1) ||
((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
/* populate blob_paths if image blob was provided as positional argument */
if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) {
blob_paths = malloc(sizeof(char *));
if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n");
exit(EXIT_FAILURE);
}
*blob_paths = argv[argc - 2];
}
/* populate arrays with zeros if they are not provided */
if (!indices)
indices = calloc(listcount, sizeof(unsigned long));
if (!instances)
instances = calloc(listcount, sizeof(unsigned long));
if (!mcounts)
mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) {
if (create_empty_capsule(argv[argc - 1], guid,
if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); }
} else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) {
} else if (create_fwbin(argv[argc - 1], blob_paths, guids,
indices, instances, fw_versions,
mcounts, listcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.30.2

Hi Sughos,
one other question. Do you know what the advantage of mkeficapsule tool over the EDK2 GenerateCapsule tool is? It seems like reinventing the wheel for me.
Best Regards Malte
Am 16.06.2023 um 14:59 schrieb Schmidt, Malte:
Hi Sughos,
Am 16.06.2023 um 14:32 schrieb Sughosh Ganu:
On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
-sughosh
thanks for the heads up. So your opinion is that we only support multiple payloads via config files and not the command line? I think it does not hurt to have both options available.
I plan to rebase my code on yours once it nears the finish line. I still have a suggsetion for it which I will post in a sec.
Best Regards Malte
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier
stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params { - bool have_header; - uint32_t fw_version; -};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'}, + {"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'}, @@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) { - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" + fprintf(stderr, "Usage: %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" - "\t-I, --instance <instance> update hardware instance\n" - "\t-v, --fw-version <version> firmware version\n" - "\t-p, --private-key <privkey file> private key file\n" - "\t-c, --certificate <cert file> signer's certificate file\n" - "\t-m, --monotonic-count <count> monotonic count\n" - "\t-d, --dump_sig dump signature (*.p7)\n" - "\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-h, --help print a help message\n", + "\t-g, --guid <guid list> comma-separated list of guids for image blob types\n" + "\t-i, --index <index list> comma-separated list of update image indices\n" + "\t-b, --image_blob <blob list> comma-separated list of image blobs\n" + "\t-I, --instance <instance list> comma-separated list of update hardware instances\n" + "\t-v, --fw-version <version list> comma-separated list of firmware versions\n" + "\t-p, --private-key <privkey file> private key file\n" + "\t-c, --certificate <cert file> signer's certificate file\n" + "\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n" + "\t-d, --dump_sig dump signature (*.p7)\n" + "\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-h, --help print a help message\n", tool_name); }
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx) * @path: Path to a capsule file * @signature: Signature data * @sig_size: Size of signature data
- @index: The payload index the signature belongs to
* * Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
- If index is negative the suffix is ".p7" (for backwards
compatibility). * * Return: * * 0 - on success * * -1 - on failure */ static int dump_signature(const char *path, const uint8_t *signature, - size_t sig_size) + size_t sig_size, int index) { char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
- sprintf(sig_path, "%s.p7", path); + if (index < 0) + sprintf(sig_path, "%s.p7", path); + else + sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err; @@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /** * create_fwbin - create an uefi capsule file * @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
* @instance: Instance number in capsule * @mcount: Monotonic count in authentication information
- @size: Size of the arrays
* @private_file: Path to a private key file * @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
* * This function actually does the job of creating an uefi capsule file. * All the arguments must be supplied. @@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(const char *path, const char *bin, - const efi_guid_t *guid, unsigned long index, - unsigned long instance, - const struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, - const char *privkey_file, const char *cert_file, - uint16_t oemflags) +static int create_fwbin(const char *path, const char **bins, + const efi_guid_t *guids, const unsigned long *indices, + const unsigned long *instances, + const unsigned long *fw_versions, const unsigned long *mcounts, + int size, const char *privkey_file, + const char *cert_file, uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; - struct efi_firmware_management_capsule_image_header image; - struct auth_context auth_context; + struct efi_firmware_management_capsule_image_header images[size]; + struct auth_context auth_contexts[size]; FILE *f; - uint8_t *data, *new_data, *buf; - off_t bin_size; - uint64_t offset; + uint8_t *data_list[size], *new_data_list[size], *buf_list[size]; + off_t bin_sizes[size]; + uint64_t offsets[size]; int ret; - struct fmp_payload_header payload_header; + struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path); - fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid); - fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance); + for (int i = 0; i < size; i++) { + fprintf(stderr, "\tpayload no: %d\n", i); + fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]); + fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]); + } #endif - auth_context.sig_size = 0; f = NULL; - data = NULL; - new_data = NULL; ret = -1;
- /* - * read a firmware binary - */ - if (read_bin_file(bin, &data, &bin_size)) - goto err; + for (int i = 0; i < size; i++) { + auth_contexts[i].sig_size = 0; + data_list[i] = NULL; + new_data_list[i] = NULL; + }
- buf = data; + for (int i = 0; i < size; i++) { + int dump_index = (size == 1) ? -1 : i;
- /* insert fmp payload header right before the payload */ - if (fmp_ph_params->have_header) { - new_data = malloc(bin_size + sizeof(payload_header)); - if (!new_data) + /* + * read a firmware binary + */ + if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
- payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; - payload_header.header_size = sizeof(payload_header); - payload_header.fw_version = fmp_ph_params->fw_version; - payload_header.lowest_supported_version = 0; /* not used */ - memcpy(new_data, &payload_header, sizeof(payload_header)); - memcpy(new_data + sizeof(payload_header), data, bin_size); - buf = new_data; - bin_size += sizeof(payload_header); - }
- /* first, calculate signature to determine its size */ - if (privkey_file && cert_file) { - auth_context.key_file = privkey_file; - auth_context.cert_file = cert_file; - auth_context.auth.monotonic_count = mcount; - auth_context.image_data = buf; - auth_context.image_size = bin_size;
- if (create_auth_data(&auth_context)) { - fprintf(stderr, "Signing firmware image failed\n"); - goto err; + buf_list[i] = data_list[i]; + /* insert fmp payload header right before the payload */ + if (fw_versions) { + new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i])); + if (!new_data_list[i]) + goto err;
+ payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE; + payload_headers[i].header_size = sizeof(payload_headers[i]); + payload_headers[i].fw_version = fw_versions[i];
- payload_headers[i].lowest_supported_version = 0; /* not used */
+ memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i])); + memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i], + bin_sizes[i]); + buf_list[i] = new_data_list[i]; + bin_sizes[i] += sizeof(payload_headers[i]); }
- if (dump_sig && - dump_signature(path, auth_context.sig_data, - auth_context.sig_size)) { - fprintf(stderr, "Creating signature file failed\n"); - goto err; + /* calculate signature to determine its size */ + if (privkey_file && cert_file) { + auth_contexts[i].key_file = privkey_file; + auth_contexts[i].cert_file = cert_file;
- auth_contexts[i].auth.monotonic_count = mcounts[i];
+ auth_contexts[i].image_data = buf_list[i]; + auth_contexts[i].image_size = bin_sizes[i];
+ if (create_auth_data(&auth_contexts[i])) { + fprintf(stderr, "Signing firmware image failed\n"); + goto err; + }
+ if (dump_sig && + dump_signature(path, auth_contexts[i].sig_data,
- auth_contexts[i].sig_size, dump_index)) {
+ fprintf(stderr, "Creating signature file failed\n"); + goto err; + } } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header) - + sizeof(capsule) + sizeof(uint64_t) - + sizeof(image) - + bin_size; - if (auth_context.sig_size) - header.capsule_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; + + sizeof(capsule) + + size * sizeof(uint64_t); /* size of item_offset_list */ + for (int i = 0; i < size; i++) { + offsets[i] = header.capsule_image_size - sizeof(header); + header.capsule_image_size += sizeof(images[i]) + + bin_sizes[i]; + if (auth_contexts[i].sig_size) + header.capsule_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + } if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err;
/* * firmware capsule header - * This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0; - capsule.payload_item_count = 1; + capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
- offset = sizeof(capsule) + sizeof(uint64_t); - if (write_capsule_file(f, &offset, sizeof(offset), - "Offset to capsule image")) + if (write_capsule_file(f, &offsets, size * sizeof(uint64_t), + "Offsets to capsule images")) goto err;
- /* - * firmware capsule image header - */ - image.version = 0x00000003; - memcpy(&image.update_image_type_id, guid, sizeof(*guid)); - image.update_image_index = index; - image.reserved[0] = 0; - image.reserved[1] = 0; - image.reserved[2] = 0; - image.update_image_size = bin_size; - if (auth_context.sig_size) - image.update_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; - image.update_vendor_code_size = 0; /* none */ - image.update_hardware_instance = instance; - image.image_capsule_support = 0; - if (auth_context.sig_size) - image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; - if (write_capsule_file(f, &image, sizeof(image), - "Firmware capsule image header")) - goto err;
- /* - * signature - */ - if (auth_context.sig_size) { - if (write_capsule_file(f, &auth_context.auth,
- sizeof(auth_context.auth),
- "Authentication header")) + for (int i = 0; i < size; i++) { + /* + * firmware capsule image header + */ + images[i].version = 0x00000003; + memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i])); + images[i].update_image_index = indices[i]; + images[i].reserved[0] = 0; + images[i].reserved[1] = 0; + images[i].reserved[2] = 0; + images[i].update_image_size = bin_sizes[i]; + if (auth_contexts[i].sig_size) + images[i].update_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + images[i].update_vendor_code_size = 0; /* none */ + images[i].update_hardware_instance = instances[i]; + images[i].image_capsule_support = 0; + if (auth_contexts[i].sig_size) + images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; + if (write_capsule_file(f, &images[i], sizeof(images[i]), + "Firmware capsule image header")) goto err;
- if (write_capsule_file(f, auth_context.sig_data,
- auth_context.sig_size, "Signature"))
+ /* + * signature + */ + if (auth_contexts[i].sig_size) { + if (write_capsule_file(f, &auth_contexts[i].auth,
- sizeof(auth_contexts[i].auth),
- "Authentication header"))
+ goto err;
+ if (write_capsule_file(f, auth_contexts[i].sig_data,
- auth_contexts[i].sig_size, "Signature"))
+ goto err; + }
+ /* + * firmware binary + */ + if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
- /* - * firmware binary - */ - if (write_capsule_file(f, buf, bin_size, "Firmware binary")) - goto err;
ret = 0; err: if (f) fclose(f); - free_sig_data(&auth_context); - free(data); - free(new_data); + for (int i = 0; i < size; i++) { + free_sig_data(&auth_contexts[i]); + free(data_list[i]); + free(new_data_list[i]); + }
return ret; } @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the
separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{ + const char *c; + int count = 0;
+ if (!*list) + return 0;
+ for (c = list; *c; c++) { + if (*c == separator) + count++; + } + /* correct count if no trailing separator present */ + if (*(c - 1) != separator) + count++;
+ return count; +}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the
list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{ + int current_count = count_items(list, separator);
+ if (*count < 0) + *count = current_count;
+ if (*count == 0 || + *count != current_count) + return -1;
+ return 0; +}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{ + const char separator_str[] = {separator, '\0'}; + char *end;
+ for (int i = 0; i < size; i++) { + elements[i] = strsep(&list, separator_str); + if (!elements[i]) + return -1; + }
+ end = strsep(&list, separator_str); /* NULL or empty string expected */ + if (end && *end) + return -1;
+ return 0; +}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- * Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{ + void *array;
+ array = malloc(count * obj_size); + if (!array) + fprintf(stderr, "Could not allocate memory for %s\n", name);
+ return array; +}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list
contains the UUIDs
- to convert and store in the array. Upon failure an error
message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size, + const char *name) +{ + efi_guid_t *guids;
+ guids = alloc_array(size, sizeof(efi_guid_t), name); + if (!guids) + return NULL;
+ for (int i = 0; i < size; i++) { + if (uuid_parse(elements[i], (unsigned char *)(guids
- i))) {
+ fprintf(stderr, "Wrong %s format\n", name); + free(guids); + return NULL; + } + convert_uuid_to_guid((unsigned char *)(guids + i)); + }
+ return guids; +}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size, + const char *name) +{ + unsigned long *array;
+ array = alloc_array(size, sizeof(unsigned long), name); + if (!array) + return NULL; + for (int i = 0; i < size; i++) + array[i] = strtoul(elements[i], NULL, 0);
+ return array; +}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the
list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- * Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator, + bool multiple_times, char *name) +{ + char **elements;
+ if (multiple_times) { + fprintf(stderr, "%s specified multiple times\n", name); + return NULL; + } + if (update_itemcount(listcount, list, separator)) { + fprintf(stderr, "List count not consistent with previous or list not provided\n"); + return NULL; + } + elements = alloc_array(*listcount, sizeof(char *), name); + if (!elements) + return NULL; + if (split_list(elements, *listcount, list, separator)) { + fprintf(stderr, "Could not parse %s list\n", name); + free(elements); + return NULL; + }
+ return elements; +}
/** * main - main entry function of mkeficapsule * @argc: Number of arguments @@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) { - efi_guid_t *guid; - unsigned char uuid_buf[16]; - unsigned long index, instance; - uint64_t mcount; + const char separator = ','; + const efi_guid_t *guids; /* an array */ + const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags; + const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file; - int c, idx; - struct fmp_payload_header_params fmp_ph_params = { 0 }; + int listcount, c, idx;
- guid = NULL; - index = 0; - instance = 0; - mcount = 0; + guids = NULL; + indices = NULL; + instances = NULL; + mcounts = NULL; + oemflags = 0; + blob_paths = NULL; privkey_file = NULL; cert_file = NULL; + elements = NULL; + listcount = -1; + fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB; - oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g': - if (guid) { - fprintf(stderr, - "Image type already specified\n"); + elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
- "GUID");
+ if (!elements) exit(EXIT_FAILURE); - } - if (uuid_parse(optarg, uuid_buf)) { - fprintf(stderr, "Wrong guid format\n");
+ guids = init_guids(elements, listcount, "GUID"); + if (!guids) exit(EXIT_FAILURE); - } - convert_uuid_to_guid(uuid_buf); - guid = (efi_guid_t *)uuid_buf;
+ free(elements); + elements = NULL; break; case 'i': - index = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!indices, "index");
+ if (!elements) + exit(EXIT_FAILURE);
+ indices = init_uls(elements, listcount, "index"); + if (!indices) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; + break; + case 'b': + blob_paths = (const char **)init_list(&listcount, optarg, separator,
- !!blob_paths, "blob path");
+ if (!blob_paths) + exit(EXIT_FAILURE); break; case 'I': - instance = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!instances, "instance");
+ if (!elements) + exit(EXIT_FAILURE);
+ instances = init_uls(elements, listcount, "instance"); + if (!instances) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'v': - fmp_ph_params.fw_version = strtoul(optarg, NULL, 0); - fmp_ph_params.have_header = true; + elements = (const char **)init_list(&listcount, optarg, separator,
- !!fw_versions, "firmware version");
+ if (!elements) + exit(EXIT_FAILURE);
+ fw_versions = init_uls(elements, listcount, "firmware version"); + if (!fw_versions) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'p': if (privkey_file) { @@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm': - mcount = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!mcounts, "monotonic count");
+ if (!elements) + exit(EXIT_FAILURE);
+ mcounts = init_uls(elements, listcount, "monotonic count"); + if (!mcounts) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'd': dump_sig = 1; @@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB && - ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file)))) || + (!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + ((argc != optind + 1) || + ((capsule_type == CAPSULE_ACCEPT) && !guids) || + ((capsule_type == CAPSULE_ACCEPT) && listcount != 1) || + ((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
+ /* populate blob_paths if image blob was provided as positional argument */ + if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) { + blob_paths = malloc(sizeof(char *)); + if (!blob_paths) { + fprintf(stderr, "Could not allocate memory for blob paths\n"); + exit(EXIT_FAILURE); + } + *blob_paths = argv[argc - 2]; + }
+ /* populate arrays with zeros if they are not provided */ + if (!indices) + indices = calloc(listcount, sizeof(unsigned long)); + if (!instances) + instances = calloc(listcount, sizeof(unsigned long)); + if (!mcounts) + mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) { - if (create_empty_capsule(argv[argc - 1], guid, + if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } - } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, - index, instance, &fmp_ph_params, mcount, privkey_file, - cert_file, (uint16_t)oemflags) < 0) { + } else if (create_fwbin(argv[argc - 1], blob_paths, guids, + indices, instances, fw_versions, + mcounts, listcount, privkey_file, + cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); } -- 2.30.2

On Fri, Jun 16, 2023 at 03:32:59PM +0200, Schmidt, Malte wrote:
Hi Sughos,
one other question. Do you know what the advantage of mkeficapsule tool over the EDK2 GenerateCapsule tool is? It seems like reinventing the wheel for me.
Simply because I didn't want to have a dependency on EDK2 package when users use U-Boot as EFI framework. (In other words, make it self-contained?)
-Takahiro Akashi
Best Regards Malte
Am 16.06.2023 um 14:59 schrieb Schmidt, Malte:
Hi Sughos,
Am 16.06.2023 um 14:32 schrieb Sughosh Ganu:
On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
-sughosh
thanks for the heads up. So your opinion is that we only support multiple payloads via config files and not the command line? I think it does not hurt to have both options available.
I plan to rebase my code on yours once it nears the finish line. I still have a suggsetion for it which I will post in a sec.
Best Regards Malte
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier
stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params { - bool have_header; - uint32_t fw_version; -};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'}, + {"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'}, @@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) { - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" + fprintf(stderr, "Usage: %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" - "\t-I, --instance <instance> update hardware instance\n" - "\t-v, --fw-version <version> firmware version\n" - "\t-p, --private-key <privkey file> private key file\n" - "\t-c, --certificate <cert file> signer's certificate file\n" - "\t-m, --monotonic-count <count> monotonic count\n" - "\t-d, --dump_sig dump signature (*.p7)\n" - "\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-h, --help print a help message\n", + "\t-g, --guid <guid list> comma-separated list of guids for image blob types\n" + "\t-i, --index <index list> comma-separated list of update image indices\n" + "\t-b, --image_blob <blob list> comma-separated list of image blobs\n" + "\t-I, --instance <instance list> comma-separated list of update hardware instances\n" + "\t-v, --fw-version <version list> comma-separated list of firmware versions\n" + "\t-p, --private-key <privkey file> private key file\n" + "\t-c, --certificate <cert file> signer's certificate file\n" + "\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n" + "\t-d, --dump_sig dump signature (*.p7)\n" + "\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-h, --help print a help message\n", tool_name); }
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx) * @path: Path to a capsule file * @signature: Signature data * @sig_size: Size of signature data
- @index: The payload index the signature belongs to
* * Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
- If index is negative the suffix is ".p7" (for backwards
compatibility). * * Return: * * 0 - on success * * -1 - on failure */ static int dump_signature(const char *path, const uint8_t *signature, - size_t sig_size) + size_t sig_size, int index) { char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
- sprintf(sig_path, "%s.p7", path); + if (index < 0) + sprintf(sig_path, "%s.p7", path); + else + sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err; @@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /** * create_fwbin - create an uefi capsule file * @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
* @instance: Instance number in capsule * @mcount: Monotonic count in authentication information
- @size: Size of the arrays
* @private_file: Path to a private key file * @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
* * This function actually does the job of creating an uefi capsule file. * All the arguments must be supplied. @@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(const char *path, const char *bin, - const efi_guid_t *guid, unsigned long index, - unsigned long instance, - const struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, - const char *privkey_file, const char *cert_file, - uint16_t oemflags) +static int create_fwbin(const char *path, const char **bins, + const efi_guid_t *guids, const unsigned long *indices, + const unsigned long *instances, + const unsigned long *fw_versions, const unsigned long *mcounts, + int size, const char *privkey_file, + const char *cert_file, uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; - struct efi_firmware_management_capsule_image_header image; - struct auth_context auth_context; + struct efi_firmware_management_capsule_image_header images[size]; + struct auth_context auth_contexts[size]; FILE *f; - uint8_t *data, *new_data, *buf; - off_t bin_size; - uint64_t offset; + uint8_t *data_list[size], *new_data_list[size], *buf_list[size]; + off_t bin_sizes[size]; + uint64_t offsets[size]; int ret; - struct fmp_payload_header payload_header; + struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path); - fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid); - fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance); + for (int i = 0; i < size; i++) { + fprintf(stderr, "\tpayload no: %d\n", i); + fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]); + fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]); + } #endif - auth_context.sig_size = 0; f = NULL; - data = NULL; - new_data = NULL; ret = -1;
- /* - * read a firmware binary - */ - if (read_bin_file(bin, &data, &bin_size)) - goto err; + for (int i = 0; i < size; i++) { + auth_contexts[i].sig_size = 0; + data_list[i] = NULL; + new_data_list[i] = NULL; + }
- buf = data; + for (int i = 0; i < size; i++) { + int dump_index = (size == 1) ? -1 : i;
- /* insert fmp payload header right before the payload */ - if (fmp_ph_params->have_header) { - new_data = malloc(bin_size + sizeof(payload_header)); - if (!new_data) + /* + * read a firmware binary + */ + if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
- payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; - payload_header.header_size = sizeof(payload_header); - payload_header.fw_version = fmp_ph_params->fw_version; - payload_header.lowest_supported_version = 0; /* not used */ - memcpy(new_data, &payload_header, sizeof(payload_header)); - memcpy(new_data + sizeof(payload_header), data, bin_size); - buf = new_data; - bin_size += sizeof(payload_header); - }
- /* first, calculate signature to determine its size */ - if (privkey_file && cert_file) { - auth_context.key_file = privkey_file; - auth_context.cert_file = cert_file; - auth_context.auth.monotonic_count = mcount; - auth_context.image_data = buf; - auth_context.image_size = bin_size;
- if (create_auth_data(&auth_context)) { - fprintf(stderr, "Signing firmware image failed\n"); - goto err; + buf_list[i] = data_list[i]; + /* insert fmp payload header right before the payload */ + if (fw_versions) { + new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i])); + if (!new_data_list[i]) + goto err;
+ payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE; + payload_headers[i].header_size = sizeof(payload_headers[i]); + payload_headers[i].fw_version = fw_versions[i];
- payload_headers[i].lowest_supported_version = 0; /* not used */
+ memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i])); + memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i], + bin_sizes[i]); + buf_list[i] = new_data_list[i]; + bin_sizes[i] += sizeof(payload_headers[i]); }
- if (dump_sig && - dump_signature(path, auth_context.sig_data, - auth_context.sig_size)) { - fprintf(stderr, "Creating signature file failed\n"); - goto err; + /* calculate signature to determine its size */ + if (privkey_file && cert_file) { + auth_contexts[i].key_file = privkey_file; + auth_contexts[i].cert_file = cert_file;
- auth_contexts[i].auth.monotonic_count = mcounts[i];
+ auth_contexts[i].image_data = buf_list[i]; + auth_contexts[i].image_size = bin_sizes[i];
+ if (create_auth_data(&auth_contexts[i])) { + fprintf(stderr, "Signing firmware image failed\n"); + goto err; + }
+ if (dump_sig && + dump_signature(path, auth_contexts[i].sig_data,
- auth_contexts[i].sig_size, dump_index)) {
+ fprintf(stderr, "Creating signature file failed\n"); + goto err; + } } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header) -
- sizeof(capsule) + sizeof(uint64_t)
- + sizeof(image) - + bin_size; - if (auth_context.sig_size) - header.capsule_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; + + sizeof(capsule) +
- size * sizeof(uint64_t); /* size of item_offset_list */
+ for (int i = 0; i < size; i++) { + offsets[i] = header.capsule_image_size - sizeof(header); + header.capsule_image_size += sizeof(images[i]) + + bin_sizes[i]; + if (auth_contexts[i].sig_size) + header.capsule_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + } if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err;
/* * firmware capsule header - * This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0; - capsule.payload_item_count = 1; + capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
- offset = sizeof(capsule) + sizeof(uint64_t); - if (write_capsule_file(f, &offset, sizeof(offset), - "Offset to capsule image")) + if (write_capsule_file(f, &offsets, size * sizeof(uint64_t), + "Offsets to capsule images")) goto err;
- /* - * firmware capsule image header - */ - image.version = 0x00000003; - memcpy(&image.update_image_type_id, guid, sizeof(*guid)); - image.update_image_index = index; - image.reserved[0] = 0; - image.reserved[1] = 0; - image.reserved[2] = 0; - image.update_image_size = bin_size; - if (auth_context.sig_size) - image.update_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; - image.update_vendor_code_size = 0; /* none */ - image.update_hardware_instance = instance; - image.image_capsule_support = 0; - if (auth_context.sig_size) - image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; - if (write_capsule_file(f, &image, sizeof(image), - "Firmware capsule image header")) - goto err;
- /* - * signature - */ - if (auth_context.sig_size) { - if (write_capsule_file(f, &auth_context.auth,
- sizeof(auth_context.auth),
- "Authentication header")) + for (int i = 0; i < size; i++) { + /* + * firmware capsule image header + */ + images[i].version = 0x00000003; + memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i])); + images[i].update_image_index = indices[i]; + images[i].reserved[0] = 0; + images[i].reserved[1] = 0; + images[i].reserved[2] = 0; + images[i].update_image_size = bin_sizes[i]; + if (auth_contexts[i].sig_size) + images[i].update_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + images[i].update_vendor_code_size = 0; /* none */ + images[i].update_hardware_instance = instances[i]; + images[i].image_capsule_support = 0; + if (auth_contexts[i].sig_size) + images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; + if (write_capsule_file(f, &images[i], sizeof(images[i]), + "Firmware capsule image header")) goto err;
- if (write_capsule_file(f, auth_context.sig_data,
- auth_context.sig_size, "Signature"))
+ /* + * signature + */ + if (auth_contexts[i].sig_size) { + if (write_capsule_file(f, &auth_contexts[i].auth,
- sizeof(auth_contexts[i].auth),
- "Authentication header"))
+ goto err;
+ if (write_capsule_file(f, auth_contexts[i].sig_data,
- auth_contexts[i].sig_size, "Signature"))
+ goto err; + }
+ /* + * firmware binary + */ + if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
- /* - * firmware binary - */ - if (write_capsule_file(f, buf, bin_size, "Firmware binary")) - goto err;
ret = 0; err: if (f) fclose(f); - free_sig_data(&auth_context); - free(data); - free(new_data); + for (int i = 0; i < size; i++) { + free_sig_data(&auth_contexts[i]); + free(data_list[i]); + free(new_data_list[i]); + }
return ret; } @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is
the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{ + const char *c; + int count = 0;
+ if (!*list) + return 0;
+ for (c = list; *c; c++) { + if (*c == separator) + count++; + } + /* correct count if no trailing separator present */ + if (*(c - 1) != separator) + count++;
+ return count; +}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with
the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{ + int current_count = count_items(list, separator);
+ if (*count < 0) + *count = current_count;
+ if (*count == 0 || + *count != current_count) + return -1;
+ return 0; +}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{ + const char separator_str[] = {separator, '\0'}; + char *end;
+ for (int i = 0; i < size; i++) { + elements[i] = strsep(&list, separator_str); + if (!elements[i]) + return -1; + }
+ end = strsep(&list, separator_str); /* NULL or empty string expected */ + if (end && *end) + return -1;
+ return 0; +}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- * Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{ + void *array;
+ array = malloc(count * obj_size); + if (!array) + fprintf(stderr, "Could not allocate memory for %s\n", name);
+ return array; +}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list
contains the UUIDs
- to convert and store in the array. Upon failure an error
message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size, + const char *name) +{ + efi_guid_t *guids;
+ guids = alloc_array(size, sizeof(efi_guid_t), name); + if (!guids) + return NULL;
+ for (int i = 0; i < size; i++) { + if (uuid_parse(elements[i], (unsigned char *)(guids + i))) { + fprintf(stderr, "Wrong %s format\n", name); + free(guids); + return NULL; + } + convert_uuid_to_guid((unsigned char *)(guids + i)); + }
+ return guids; +}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size, + const char *name) +{ + unsigned long *array;
+ array = alloc_array(size, sizeof(unsigned long), name); + if (!array) + return NULL; + for (int i = 0; i < size; i++) + array[i] = strtoul(elements[i], NULL, 0);
+ return array; +}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with
the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- * Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator, + bool multiple_times, char *name) +{ + char **elements;
+ if (multiple_times) { + fprintf(stderr, "%s specified multiple times\n", name); + return NULL; + } + if (update_itemcount(listcount, list, separator)) { + fprintf(stderr, "List count not consistent with previous or list not provided\n"); + return NULL; + } + elements = alloc_array(*listcount, sizeof(char *), name); + if (!elements) + return NULL; + if (split_list(elements, *listcount, list, separator)) { + fprintf(stderr, "Could not parse %s list\n", name); + free(elements); + return NULL; + }
+ return elements; +}
/** * main - main entry function of mkeficapsule * @argc: Number of arguments @@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) { - efi_guid_t *guid; - unsigned char uuid_buf[16]; - unsigned long index, instance; - uint64_t mcount; + const char separator = ','; + const efi_guid_t *guids; /* an array */ + const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags; + const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file; - int c, idx; - struct fmp_payload_header_params fmp_ph_params = { 0 }; + int listcount, c, idx;
- guid = NULL; - index = 0; - instance = 0; - mcount = 0; + guids = NULL; + indices = NULL; + instances = NULL; + mcounts = NULL; + oemflags = 0; + blob_paths = NULL; privkey_file = NULL; cert_file = NULL; + elements = NULL; + listcount = -1; + fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB; - oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g': - if (guid) { - fprintf(stderr, - "Image type already specified\n"); + elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
- "GUID");
+ if (!elements) exit(EXIT_FAILURE); - } - if (uuid_parse(optarg, uuid_buf)) { - fprintf(stderr, "Wrong guid format\n");
+ guids = init_guids(elements, listcount, "GUID"); + if (!guids) exit(EXIT_FAILURE); - } - convert_uuid_to_guid(uuid_buf); - guid = (efi_guid_t *)uuid_buf;
+ free(elements); + elements = NULL; break; case 'i': - index = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!indices, "index");
+ if (!elements) + exit(EXIT_FAILURE);
+ indices = init_uls(elements, listcount, "index"); + if (!indices) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; + break; + case 'b': + blob_paths = (const char **)init_list(&listcount, optarg, separator,
- !!blob_paths, "blob path");
+ if (!blob_paths) + exit(EXIT_FAILURE); break; case 'I': - instance = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!instances, "instance");
+ if (!elements) + exit(EXIT_FAILURE);
+ instances = init_uls(elements, listcount, "instance"); + if (!instances) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'v': - fmp_ph_params.fw_version = strtoul(optarg, NULL, 0); - fmp_ph_params.have_header = true; + elements = (const char **)init_list(&listcount, optarg, separator,
- !!fw_versions, "firmware version");
+ if (!elements) + exit(EXIT_FAILURE);
+ fw_versions = init_uls(elements, listcount, "firmware version"); + if (!fw_versions) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'p': if (privkey_file) { @@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm': - mcount = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!mcounts, "monotonic count");
+ if (!elements) + exit(EXIT_FAILURE);
+ mcounts = init_uls(elements, listcount, "monotonic count"); + if (!mcounts) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'd': dump_sig = 1; @@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB && - ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file)))) || + (!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + ((argc != optind + 1) || + ((capsule_type == CAPSULE_ACCEPT) && !guids) || + ((capsule_type == CAPSULE_ACCEPT) && listcount != 1) || + ((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
+ /* populate blob_paths if image blob was provided as positional argument */ + if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) { + blob_paths = malloc(sizeof(char *)); + if (!blob_paths) { + fprintf(stderr, "Could not allocate memory for blob paths\n"); + exit(EXIT_FAILURE); + } + *blob_paths = argv[argc - 2]; + }
+ /* populate arrays with zeros if they are not provided */ + if (!indices) + indices = calloc(listcount, sizeof(unsigned long)); + if (!instances) + instances = calloc(listcount, sizeof(unsigned long)); + if (!mcounts) + mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) { - if (create_empty_capsule(argv[argc - 1], guid, + if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } - } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, - index, instance, &fmp_ph_params, mcount, privkey_file, - cert_file, (uint16_t)oemflags) < 0) { + } else if (create_fwbin(argv[argc - 1], blob_paths, guids, + indices, instances, fw_versions, + mcounts, listcount, privkey_file, + cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); } -- 2.30.2

Hello Takahiro,
Am 17.06.2023 um 02:37 schrieb AKASHI Takahiro:
On Fri, Jun 16, 2023 at 03:32:59PM +0200, Schmidt, Malte wrote:
Hi Sughos,
one other question. Do you know what the advantage of mkeficapsule tool over the EDK2 GenerateCapsule tool is? It seems like reinventing the wheel for me.
Simply because I didn't want to have a dependency on EDK2 package when users use U-Boot as EFI framework. (In other words, make it self-contained?)
-Takahiro Akashi
thanks for the clarification.
Best Regards Malte
Best Regards Malte
Am 16.06.2023 um 14:59 schrieb Schmidt, Malte:
Hi Sughos,
Am 16.06.2023 um 14:32 schrieb Sughosh Ganu:
On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
-sughosh
thanks for the heads up. So your opinion is that we only support multiple payloads via config files and not the command line? I think it does not hurt to have both options available.
I plan to rebase my code on yours once it nears the finish line. I still have a suggsetion for it which I will post in a sec.
Best Regards Malte
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier
stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params { - bool have_header; - uint32_t fw_version; -};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'}, + {"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'}, @@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) { - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" + fprintf(stderr, "Usage: %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" - "\t-I, --instance <instance> update hardware instance\n" - "\t-v, --fw-version <version> firmware version\n" - "\t-p, --private-key <privkey file> private key file\n" - "\t-c, --certificate <cert file> signer's certificate file\n" - "\t-m, --monotonic-count <count> monotonic count\n" - "\t-d, --dump_sig dump signature (*.p7)\n" - "\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-h, --help print a help message\n", + "\t-g, --guid <guid list> comma-separated list of guids for image blob types\n" + "\t-i, --index <index list> comma-separated list of update image indices\n" + "\t-b, --image_blob <blob list> comma-separated list of image blobs\n" + "\t-I, --instance <instance list> comma-separated list of update hardware instances\n" + "\t-v, --fw-version <version list> comma-separated list of firmware versions\n" + "\t-p, --private-key <privkey file> private key file\n" + "\t-c, --certificate <cert file> signer's certificate file\n" + "\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n" + "\t-d, --dump_sig dump signature (*.p7)\n" + "\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-h, --help print a help message\n", tool_name); }
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx) * @path: Path to a capsule file * @signature: Signature data * @sig_size: Size of signature data
- @index: The payload index the signature belongs to
* * Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
- If index is negative the suffix is ".p7" (for backwards
compatibility). * * Return: * * 0 - on success * * -1 - on failure */ static int dump_signature(const char *path, const uint8_t *signature, - size_t sig_size) + size_t sig_size, int index) { char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
- sprintf(sig_path, "%s.p7", path); + if (index < 0) + sprintf(sig_path, "%s.p7", path); + else + sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err; @@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /** * create_fwbin - create an uefi capsule file * @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
* @instance: Instance number in capsule * @mcount: Monotonic count in authentication information
- @size: Size of the arrays
* @private_file: Path to a private key file * @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
* * This function actually does the job of creating an uefi capsule file. * All the arguments must be supplied. @@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(const char *path, const char *bin, - const efi_guid_t *guid, unsigned long index, - unsigned long instance, - const struct fmp_payload_header_params *fmp_ph_params, - uint64_t mcount, - const char *privkey_file, const char *cert_file, - uint16_t oemflags) +static int create_fwbin(const char *path, const char **bins, + const efi_guid_t *guids, const unsigned long *indices, + const unsigned long *instances, + const unsigned long *fw_versions, const unsigned long *mcounts, + int size, const char *privkey_file, + const char *cert_file, uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; - struct efi_firmware_management_capsule_image_header image; - struct auth_context auth_context; + struct efi_firmware_management_capsule_image_header images[size]; + struct auth_context auth_contexts[size]; FILE *f; - uint8_t *data, *new_data, *buf; - off_t bin_size; - uint64_t offset; + uint8_t *data_list[size], *new_data_list[size], *buf_list[size]; + off_t bin_sizes[size]; + uint64_t offsets[size]; int ret; - struct fmp_payload_header payload_header; + struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path); - fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid); - fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance); + for (int i = 0; i < size; i++) { + fprintf(stderr, "\tpayload no: %d\n", i); + fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]); + fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]); + } #endif - auth_context.sig_size = 0; f = NULL; - data = NULL; - new_data = NULL; ret = -1;
- /* - * read a firmware binary - */ - if (read_bin_file(bin, &data, &bin_size)) - goto err; + for (int i = 0; i < size; i++) { + auth_contexts[i].sig_size = 0; + data_list[i] = NULL; + new_data_list[i] = NULL; + }
- buf = data; + for (int i = 0; i < size; i++) { + int dump_index = (size == 1) ? -1 : i;
- /* insert fmp payload header right before the payload */ - if (fmp_ph_params->have_header) { - new_data = malloc(bin_size + sizeof(payload_header)); - if (!new_data) + /* + * read a firmware binary + */ + if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
- payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; - payload_header.header_size = sizeof(payload_header); - payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */ - memcpy(new_data, &payload_header, sizeof(payload_header)); - memcpy(new_data + sizeof(payload_header), data, bin_size); - buf = new_data; - bin_size += sizeof(payload_header); - }
- /* first, calculate signature to determine its size */ - if (privkey_file && cert_file) { - auth_context.key_file = privkey_file; - auth_context.cert_file = cert_file; - auth_context.auth.monotonic_count = mcount; - auth_context.image_data = buf; - auth_context.image_size = bin_size;
- if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n"); - goto err; + buf_list[i] = data_list[i]; + /* insert fmp payload header right before the payload */ + if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i])); + if (!new_data_list[i]) + goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
- payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i], + bin_sizes[i]); + buf_list[i] = new_data_list[i]; + bin_sizes[i] += sizeof(payload_headers[i]); }
- if (dump_sig && - dump_signature(path, auth_context.sig_data, - auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n"); - goto err; + /* calculate signature to determine its size */ + if (privkey_file && cert_file) { + auth_contexts[i].key_file = privkey_file; + auth_contexts[i].cert_file = cert_file;
- auth_contexts[i].auth.monotonic_count = mcounts[i];
+ auth_contexts[i].image_data = buf_list[i]; + auth_contexts[i].image_size = bin_sizes[i];
+ if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n"); + goto err; + }
+ if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
- auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n"); + goto err; + } } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
- sizeof(capsule) + sizeof(uint64_t)
- + sizeof(image) - + bin_size; - if (auth_context.sig_size) - header.capsule_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; + + sizeof(capsule)
- size * sizeof(uint64_t); /* size of item_offset_list */
+ for (int i = 0; i < size; i++) { + offsets[i] = header.capsule_image_size - sizeof(header); + header.capsule_image_size += sizeof(images[i]) + + bin_sizes[i]; + if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + } if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err;
/* * firmware capsule header - * This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0; - capsule.payload_item_count = 1; + capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
- offset = sizeof(capsule) + sizeof(uint64_t); - if (write_capsule_file(f, &offset, sizeof(offset), - "Offset to capsule image")) + if (write_capsule_file(f, &offsets, size * sizeof(uint64_t), + "Offsets to capsule images")) goto err;
- /* - * firmware capsule image header - */ - image.version = 0x00000003; - memcpy(&image.update_image_type_id, guid, sizeof(*guid)); - image.update_image_index = index; - image.reserved[0] = 0; - image.reserved[1] = 0; - image.reserved[2] = 0; - image.update_image_size = bin_size; - if (auth_context.sig_size) - image.update_image_size += sizeof(auth_context.auth) - + auth_context.sig_size; - image.update_vendor_code_size = 0; /* none */ - image.update_hardware_instance = instance; - image.image_capsule_support = 0; - if (auth_context.sig_size) - image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; - if (write_capsule_file(f, &image, sizeof(image), - "Firmware capsule image header")) - goto err;
- /* - * signature - */ - if (auth_context.sig_size) { - if (write_capsule_file(f, &auth_context.auth,
- sizeof(auth_context.auth),
- "Authentication header")) + for (int i = 0; i < size; i++) { + /* + * firmware capsule image header + */ + images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i])); + images[i].update_image_index = indices[i]; + images[i].reserved[0] = 0; + images[i].reserved[1] = 0; + images[i].reserved[2] = 0; + images[i].update_image_size = bin_sizes[i]; + if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth) + + auth_contexts[i].sig_size; + images[i].update_vendor_code_size = 0; /* none */ + images[i].update_hardware_instance = instances[i]; + images[i].image_capsule_support = 0; + if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; + if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
- if (write_capsule_file(f, auth_context.sig_data,
- auth_context.sig_size, "Signature"))
+ /* + * signature + */ + if (auth_contexts[i].sig_size) { + if (write_capsule_file(f, &auth_contexts[i].auth,
- sizeof(auth_contexts[i].auth),
- "Authentication header"))
+ goto err;
+ if (write_capsule_file(f, auth_contexts[i].sig_data,
- auth_contexts[i].sig_size, "Signature"))
+ goto err; + }
+ /* + * firmware binary + */ + if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
- /* - * firmware binary - */ - if (write_capsule_file(f, buf, bin_size, "Firmware binary")) - goto err;
ret = 0; err: if (f) fclose(f); - free_sig_data(&auth_context); - free(data); - free(new_data); + for (int i = 0; i < size; i++) { + free_sig_data(&auth_contexts[i]); + free(data_list[i]); + free(new_data_list[i]); + }
return ret; } @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is
the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{ + const char *c; + int count = 0;
+ if (!*list) + return 0;
+ for (c = list; *c; c++) { + if (*c == separator) + count++; + } + /* correct count if no trailing separator present */ + if (*(c - 1) != separator) + count++;
+ return count; +}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with
the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{ + int current_count = count_items(list, separator);
+ if (*count < 0) + *count = current_count;
+ if (*count == 0 || + *count != current_count) + return -1;
+ return 0; +}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{ + const char separator_str[] = {separator, '\0'}; + char *end;
+ for (int i = 0; i < size; i++) { + elements[i] = strsep(&list, separator_str); + if (!elements[i]) + return -1; + }
+ end = strsep(&list, separator_str); /* NULL or empty string expected */ + if (end && *end) + return -1;
+ return 0; +}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- * Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{ + void *array;
+ array = malloc(count * obj_size); + if (!array) + fprintf(stderr, "Could not allocate memory for %s\n", name);
+ return array; +}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list
contains the UUIDs
- to convert and store in the array. Upon failure an error
message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size, + const char *name) +{ + efi_guid_t *guids;
+ guids = alloc_array(size, sizeof(efi_guid_t), name); + if (!guids) + return NULL;
+ for (int i = 0; i < size; i++) { + if (uuid_parse(elements[i], (unsigned char *)(guids + i))) { + fprintf(stderr, "Wrong %s format\n", name); + free(guids); + return NULL; + } + convert_uuid_to_guid((unsigned char *)(guids + i)); + }
+ return guids; +}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size, + const char *name) +{ + unsigned long *array;
+ array = alloc_array(size, sizeof(unsigned long), name); + if (!array) + return NULL; + for (int i = 0; i < size; i++) + array[i] = strtoul(elements[i], NULL, 0);
+ return array; +}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with
the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- * Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator, + bool multiple_times, char *name) +{ + char **elements;
+ if (multiple_times) { + fprintf(stderr, "%s specified multiple times\n", name); + return NULL; + } + if (update_itemcount(listcount, list, separator)) { + fprintf(stderr, "List count not consistent with previous or list not provided\n"); + return NULL; + } + elements = alloc_array(*listcount, sizeof(char *), name); + if (!elements) + return NULL; + if (split_list(elements, *listcount, list, separator)) { + fprintf(stderr, "Could not parse %s list\n", name); + free(elements); + return NULL; + }
+ return elements; +}
/** * main - main entry function of mkeficapsule * @argc: Number of arguments @@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) { - efi_guid_t *guid; - unsigned char uuid_buf[16]; - unsigned long index, instance; - uint64_t mcount; + const char separator = ','; + const efi_guid_t *guids; /* an array */ + const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags; + const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file; - int c, idx; - struct fmp_payload_header_params fmp_ph_params = { 0 }; + int listcount, c, idx;
- guid = NULL; - index = 0; - instance = 0; - mcount = 0; + guids = NULL; + indices = NULL; + instances = NULL; + mcounts = NULL; + oemflags = 0; + blob_paths = NULL; privkey_file = NULL; cert_file = NULL; + elements = NULL; + listcount = -1; + fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB; - oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g': - if (guid) { - fprintf(stderr,
"Image type already specified\n"); + elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
- "GUID");
+ if (!elements) exit(EXIT_FAILURE); - } - if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
+ guids = init_guids(elements, listcount, "GUID"); + if (!guids) exit(EXIT_FAILURE); - } - convert_uuid_to_guid(uuid_buf); - guid = (efi_guid_t *)uuid_buf;
+ free(elements); + elements = NULL; break; case 'i': - index = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!indices, "index");
+ if (!elements) + exit(EXIT_FAILURE);
+ indices = init_uls(elements, listcount, "index"); + if (!indices) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; + break; + case 'b': + blob_paths = (const char **)init_list(&listcount, optarg, separator,
- !!blob_paths, "blob path");
+ if (!blob_paths) + exit(EXIT_FAILURE); break; case 'I': - instance = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!instances, "instance");
+ if (!elements) + exit(EXIT_FAILURE);
+ instances = init_uls(elements, listcount, "instance"); + if (!instances) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'v':
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0); - fmp_ph_params.have_header = true; + elements = (const char **)init_list(&listcount, optarg, separator,
- !!fw_versions, "firmware version");
+ if (!elements) + exit(EXIT_FAILURE);
+ fw_versions = init_uls(elements, listcount, "firmware version"); + if (!fw_versions) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'p': if (privkey_file) { @@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm': - mcount = strtoul(optarg, NULL, 0); + elements = (const char **)init_list(&listcount, optarg, separator,
- !!mcounts, "monotonic count");
+ if (!elements) + exit(EXIT_FAILURE);
+ mcounts = init_uls(elements, listcount, "monotonic count"); + if (!mcounts) + exit(EXIT_FAILURE);
+ free(elements); + elements = NULL; break; case 'd': dump_sig = 1; @@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB && - ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file)))) || + (!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + ((argc != optind + 1) || + ((capsule_type == CAPSULE_ACCEPT) && !guids) || + ((capsule_type == CAPSULE_ACCEPT) && listcount != 1) || + ((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
+ /* populate blob_paths if image blob was provided as positional argument */ + if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) { + blob_paths = malloc(sizeof(char *)); + if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n"); + exit(EXIT_FAILURE); + } + *blob_paths = argv[argc - 2]; + }
+ /* populate arrays with zeros if they are not provided */ + if (!indices) + indices = calloc(listcount, sizeof(unsigned long)); + if (!instances) + instances = calloc(listcount, sizeof(unsigned long)); + if (!mcounts) + mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) { - if (create_empty_capsule(argv[argc - 1], guid, + if (create_empty_capsule(argv[argc - 1], guids,
capsule_type == CAPSULE_ACCEPT) < 0) {
fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } - } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file, - cert_file, (uint16_t)oemflags) < 0) { + } else if (create_fwbin(argv[argc - 1], blob_paths, guids, + indices, instances, fw_versions, + mcounts, listcount, privkey_file, + cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); } -- 2.30.2

On Fri, Jun 16, 2023 at 06:02:52PM +0530, Sughosh Ganu wrote:
On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
As I said in my reply to the patch[0/5], I don't think we have a strong reason to support multiple images because there is already a FIT-based capsule support. That said, if there is a good reason to do so, Sughosh's suggestion makes much sense to me.
BTW, sughosh's patch implements yet another key:value format for config files. I wondered if we could use a generic (standardized) format, like a device tree or yaml, or others.
-Takahiro Akashi
-sughosh
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params {
bool have_header;
uint32_t fw_version;
-};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'},
{"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'},
@@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
fprintf(stderr, "Usage: %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"
"\t-I, --instance <instance> update hardware instance\n"
"\t-v, --fw-version <version> firmware version\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n",
"\t-g, --guid <guid list> comma-separated list of guids for image blob types\n"
"\t-i, --index <index list> comma-separated list of update image indices\n"
"\t-b, --image_blob <blob list> comma-separated list of image blobs\n"
"\t-I, --instance <instance list> comma-separated list of update hardware instances\n"
"\t-v, --fw-version <version list> comma-separated list of firmware versions\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n", tool_name);
}
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx)
- @path: Path to a capsule file
- @signature: Signature data
- @sig_size: Size of signature data
- @index: The payload index the signature belongs to
- Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
*/
- If index is negative the suffix is ".p7" (for backwards compatibility).
- Return:
- 0 - on success
- -1 - on failure
static int dump_signature(const char *path, const uint8_t *signature,
size_t sig_size)
size_t sig_size, int index)
{ char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
sprintf(sig_path, "%s.p7", path);
if (index < 0)
sprintf(sig_path, "%s.p7", path);
else
sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err;
@@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /**
- create_fwbin - create an uefi capsule file
- @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
- @instance: Instance number in capsule
- @mcount: Monotonic count in authentication information
- @size: Size of the arrays
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file,
uint16_t oemflags)
+static int create_fwbin(const char *path, const char **bins,
const efi_guid_t *guids, const unsigned long *indices,
const unsigned long *instances,
const unsigned long *fw_versions, const unsigned long *mcounts,
int size, const char *privkey_file,
const char *cert_file, uint16_t oemflags)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule;
struct efi_firmware_management_capsule_image_header image;
struct auth_context auth_context;
struct efi_firmware_management_capsule_image_header images[size];
struct auth_context auth_contexts[size]; FILE *f;
uint8_t *data, *new_data, *buf;
off_t bin_size;
uint64_t offset;
uint8_t *data_list[size], *new_data_list[size], *buf_list[size];
off_t bin_sizes[size];
uint64_t offsets[size]; int ret;
struct fmp_payload_header payload_header;
struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path);
fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
for (int i = 0; i < size; i++) {
fprintf(stderr, "\tpayload no: %d\n", i);
fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]);
fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]);
}
#endif
auth_context.sig_size = 0; f = NULL;
data = NULL;
new_data = NULL; ret = -1;
/*
* read a firmware binary
*/
if (read_bin_file(bin, &data, &bin_size))
goto err;
for (int i = 0; i < size; i++) {
auth_contexts[i].sig_size = 0;
data_list[i] = NULL;
new_data_list[i] = NULL;
}
buf = data;
for (int i = 0; i < size; i++) {
int dump_index = (size == 1) ? -1 : i;
/* insert fmp payload header right before the payload */
if (fmp_ph_params->have_header) {
new_data = malloc(bin_size + sizeof(payload_header));
if (!new_data)
/*
* read a firmware binary
*/
if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_header.header_size = sizeof(payload_header);
payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */
memcpy(new_data, &payload_header, sizeof(payload_header));
memcpy(new_data + sizeof(payload_header), data, bin_size);
buf = new_data;
bin_size += sizeof(payload_header);
}
/* first, calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = buf;
auth_context.image_size = bin_size;
if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
buf_list[i] = data_list[i];
/* insert fmp payload header right before the payload */
if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i]));
if (!new_data_list[i])
goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i],
bin_sizes[i]);
buf_list[i] = new_data_list[i];
bin_sizes[i] += sizeof(payload_headers[i]); }
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
/* calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_contexts[i].key_file = privkey_file;
auth_contexts[i].cert_file = cert_file;
auth_contexts[i].auth.monotonic_count = mcounts[i];
auth_contexts[i].image_data = buf_list[i];
auth_contexts[i].image_size = bin_sizes[i];
if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
}
if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
} } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
+ sizeof(capsule) + sizeof(uint64_t)
+ sizeof(image)
+ bin_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
+ sizeof(capsule)
+ size * sizeof(uint64_t); /* size of item_offset_list */
for (int i = 0; i < size; i++) {
offsets[i] = header.capsule_image_size - sizeof(header);
header.capsule_image_size += sizeof(images[i])
+ bin_sizes[i];
if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
} if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err; /* * firmware capsule header
* This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0;
capsule.payload_item_count = 1;
capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
offset = sizeof(capsule) + sizeof(uint64_t);
if (write_capsule_file(f, &offset, sizeof(offset),
"Offset to capsule image"))
if (write_capsule_file(f, &offsets, size * sizeof(uint64_t),
"Offsets to capsule images")) goto err;
/*
* firmware capsule image header
*/
image.version = 0x00000003;
memcpy(&image.update_image_type_id, guid, sizeof(*guid));
image.update_image_index = index;
image.reserved[0] = 0;
image.reserved[1] = 0;
image.reserved[2] = 0;
image.update_image_size = bin_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */
image.update_hardware_instance = instance;
image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &image, sizeof(image),
"Firmware capsule image header"))
goto err;
/*
* signature
*/
if (auth_context.sig_size) {
if (write_capsule_file(f, &auth_context.auth,
sizeof(auth_context.auth),
"Authentication header"))
for (int i = 0; i < size; i++) {
/*
* firmware capsule image header
*/
images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i]));
images[i].update_image_index = indices[i];
images[i].reserved[0] = 0;
images[i].reserved[1] = 0;
images[i].reserved[2] = 0;
images[i].update_image_size = bin_sizes[i];
if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
images[i].update_vendor_code_size = 0; /* none */
images[i].update_hardware_instance = instances[i];
images[i].image_capsule_support = 0;
if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
if (write_capsule_file(f, auth_context.sig_data,
auth_context.sig_size, "Signature"))
/*
* signature
*/
if (auth_contexts[i].sig_size) {
if (write_capsule_file(f, &auth_contexts[i].auth,
sizeof(auth_contexts[i].auth),
"Authentication header"))
goto err;
if (write_capsule_file(f, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, "Signature"))
goto err;
}
/*
* firmware binary
*/
if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
/*
* firmware binary
*/
if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
goto err;
ret = 0;
err: if (f) fclose(f);
free_sig_data(&auth_context);
free(data);
free(new_data);
for (int i = 0; i < size; i++) {
free_sig_data(&auth_contexts[i]);
free(data_list[i]);
free(new_data_list[i]);
} return ret;
} @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{
const char *c;
int count = 0;
if (!*list)
return 0;
for (c = list; *c; c++) {
if (*c == separator)
count++;
}
/* correct count if no trailing separator present */
if (*(c - 1) != separator)
count++;
return count;
+}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{
int current_count = count_items(list, separator);
if (*count < 0)
*count = current_count;
if (*count == 0 ||
*count != current_count)
return -1;
return 0;
+}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{
const char separator_str[] = {separator, '\0'};
char *end;
for (int i = 0; i < size; i++) {
elements[i] = strsep(&list, separator_str);
if (!elements[i])
return -1;
}
end = strsep(&list, separator_str); /* NULL or empty string expected */
if (end && *end)
return -1;
return 0;
+}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{
void *array;
array = malloc(count * obj_size);
if (!array)
fprintf(stderr, "Could not allocate memory for %s\n", name);
return array;
+}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list contains the UUIDs
- to convert and store in the array. Upon failure an error message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size,
const char *name)
+{
efi_guid_t *guids;
guids = alloc_array(size, sizeof(efi_guid_t), name);
if (!guids)
return NULL;
for (int i = 0; i < size; i++) {
if (uuid_parse(elements[i], (unsigned char *)(guids + i))) {
fprintf(stderr, "Wrong %s format\n", name);
free(guids);
return NULL;
}
convert_uuid_to_guid((unsigned char *)(guids + i));
}
return guids;
+}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size,
const char *name)
+{
unsigned long *array;
array = alloc_array(size, sizeof(unsigned long), name);
if (!array)
return NULL;
for (int i = 0; i < size; i++)
array[i] = strtoul(elements[i], NULL, 0);
return array;
+}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator,
bool multiple_times, char *name)
+{
char **elements;
if (multiple_times) {
fprintf(stderr, "%s specified multiple times\n", name);
return NULL;
}
if (update_itemcount(listcount, list, separator)) {
fprintf(stderr, "List count not consistent with previous or list not provided\n");
return NULL;
}
elements = alloc_array(*listcount, sizeof(char *), name);
if (!elements)
return NULL;
if (split_list(elements, *listcount, list, separator)) {
fprintf(stderr, "Could not parse %s list\n", name);
free(elements);
return NULL;
}
return elements;
+}
/**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) {
efi_guid_t *guid;
unsigned char uuid_buf[16];
unsigned long index, instance;
uint64_t mcount;
const char separator = ',';
const efi_guid_t *guids; /* an array */
const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags;
const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file;
int c, idx;
struct fmp_payload_header_params fmp_ph_params = { 0 };
int listcount, c, idx;
guid = NULL;
index = 0;
instance = 0;
mcount = 0;
guids = NULL;
indices = NULL;
instances = NULL;
mcounts = NULL;
oemflags = 0;
blob_paths = NULL; privkey_file = NULL; cert_file = NULL;
elements = NULL;
listcount = -1;
fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g':
if (guid) {
fprintf(stderr,
"Image type already specified\n");
elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
"GUID");
if (!elements) exit(EXIT_FAILURE);
}
if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
guids = init_guids(elements, listcount, "GUID");
if (!guids) exit(EXIT_FAILURE);
}
convert_uuid_to_guid(uuid_buf);
guid = (efi_guid_t *)uuid_buf;
free(elements);
elements = NULL; break; case 'i':
index = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!indices, "index");
if (!elements)
exit(EXIT_FAILURE);
indices = init_uls(elements, listcount, "index");
if (!indices)
exit(EXIT_FAILURE);
free(elements);
elements = NULL;
break;
case 'b':
blob_paths = (const char **)init_list(&listcount, optarg, separator,
!!blob_paths, "blob path");
if (!blob_paths)
exit(EXIT_FAILURE); break; case 'I':
instance = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!instances, "instance");
if (!elements)
exit(EXIT_FAILURE);
instances = init_uls(elements, listcount, "instance");
if (!instances)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'v':
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
fmp_ph_params.have_header = true;
elements = (const char **)init_list(&listcount, optarg, separator,
!!fw_versions, "firmware version");
if (!elements)
exit(EXIT_FAILURE);
fw_versions = init_uls(elements, listcount, "firmware version");
if (!fw_versions)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'p': if (privkey_file) {
@@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm':
mcount = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!mcounts, "monotonic count");
if (!elements)
exit(EXIT_FAILURE);
mcounts = init_uls(elements, listcount, "monotonic count");
if (!mcounts)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'd': dump_sig = 1;
@@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB &&
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guid) ||
((capsule_type == CAPSULE_REVERT) && guid)))) {
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guids) ||
((capsule_type == CAPSULE_ACCEPT) && listcount != 1) ||
((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
/* populate blob_paths if image blob was provided as positional argument */
if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) {
blob_paths = malloc(sizeof(char *));
if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n");
exit(EXIT_FAILURE);
}
*blob_paths = argv[argc - 2];
}
/* populate arrays with zeros if they are not provided */
if (!indices)
indices = calloc(listcount, sizeof(unsigned long));
if (!instances)
instances = calloc(listcount, sizeof(unsigned long));
if (!mcounts)
mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) {
if (create_empty_capsule(argv[argc - 1], guid,
if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); }
} else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) {
} else if (create_fwbin(argv[argc - 1], blob_paths, guids,
indices, instances, fw_versions,
mcounts, listcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.30.2

On Sat, 17 Jun 2023 at 06:26, AKASHI Takahiro takahiro.akashi@linaro.org wrote:
On Fri, Jun 16, 2023 at 06:02:52PM +0530, Sughosh Ganu wrote:
On Fri, 16 Jun 2023 at 17:56, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Stefan,
On Fri, 16 Jun 2023 at 17:04, Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss@weidmueller.com wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
I am trying to upstream support for specifying the capsule parameters for multiple payloads through a config file [1]. This is on similar lines to the support in the Edk2 GenerateCapule tool where multiple payloads can be specified through a json file. I think you can base your changes on my series.
Btw, with the support being added for getting the capsule parameters through a config file, I believe your changes would be pretty much simplified. Instead of passing all those parameters through the command line, they can instead be read from the config file and used to generate a single capsule file consisting of multiple payloads. That would be a much simpler implementation.
As I said in my reply to the patch[0/5], I don't think we have a strong reason to support multiple images because there is already a FIT-based capsule support. That said, if there is a good reason to do so, Sughosh's suggestion makes much sense to me.
BTW, sughosh's patch implements yet another key:value format for config files. I wondered if we could use a generic (standardized) format, like a device tree or yaml, or others.
I chose the key:value pairs primarily because I wanted to keep the syntax of the config file as similar to the one in EDK2 as possible. I believe keeping the format simple is better especially when we are not dealing with multiple values, or an array of u32 cells like in device tree.
-sughosh
-Takahiro Akashi
-sughosh
-sughosh
[1] - https://lore.kernel.org/u-boot/20230613103806.812065-1-sughosh.ganu@linaro.o...
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params {
bool have_header;
uint32_t fw_version;
-};
#endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'},
{"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'},
@@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
fprintf(stderr, "Usage: %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"
"\t-I, --instance <instance> update hardware instance\n"
"\t-v, --fw-version <version> firmware version\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n",
"\t-g, --guid <guid list> comma-separated list of guids for image blob types\n"
"\t-i, --index <index list> comma-separated list of update image indices\n"
"\t-b, --image_blob <blob list> comma-separated list of image blobs\n"
"\t-I, --instance <instance list> comma-separated list of update hardware instances\n"
"\t-v, --fw-version <version list> comma-separated list of firmware versions\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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-h, --help print a help message\n", tool_name);
}
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx)
- @path: Path to a capsule file
- @signature: Signature data
- @sig_size: Size of signature data
- @index: The payload index the signature belongs to
- Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
*/
- If index is negative the suffix is ".p7" (for backwards compatibility).
- Return:
- 0 - on success
- -1 - on failure
static int dump_signature(const char *path, const uint8_t *signature,
size_t sig_size)
size_t sig_size, int index)
{ char *sig_path; FILE *f; @@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
sprintf(sig_path, "%s.p7", path);
if (index < 0)
sprintf(sig_path, "%s.p7", path);
else
sprintf(sig_path, "%s_%d.p7", path, index);
f = fopen(sig_path, "w"); if (!f) goto err;
@@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /**
- create_fwbin - create an uefi capsule file
- @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
- @instance: Instance number in capsule
- @mcount: Monotonic count in authentication information
- @size: Size of the arrays
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file,
uint16_t oemflags)
+static int create_fwbin(const char *path, const char **bins,
const efi_guid_t *guids, const unsigned long *indices,
const unsigned long *instances,
const unsigned long *fw_versions, const unsigned long *mcounts,
int size, const char *privkey_file,
const char *cert_file, uint16_t oemflags)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule;
struct efi_firmware_management_capsule_image_header image;
struct auth_context auth_context;
struct efi_firmware_management_capsule_image_header images[size];
struct auth_context auth_contexts[size]; FILE *f;
uint8_t *data, *new_data, *buf;
off_t bin_size;
uint64_t offset;
uint8_t *data_list[size], *new_data_list[size], *buf_list[size];
off_t bin_sizes[size];
uint64_t offsets[size]; int ret;
struct fmp_payload_header payload_header;
struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path);
fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
for (int i = 0; i < size; i++) {
fprintf(stderr, "\tpayload no: %d\n", i);
fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]);
fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]);
}
#endif
auth_context.sig_size = 0; f = NULL;
data = NULL;
new_data = NULL; ret = -1;
/*
* read a firmware binary
*/
if (read_bin_file(bin, &data, &bin_size))
goto err;
for (int i = 0; i < size; i++) {
auth_contexts[i].sig_size = 0;
data_list[i] = NULL;
new_data_list[i] = NULL;
}
buf = data;
for (int i = 0; i < size; i++) {
int dump_index = (size == 1) ? -1 : i;
/* insert fmp payload header right before the payload */
if (fmp_ph_params->have_header) {
new_data = malloc(bin_size + sizeof(payload_header));
if (!new_data)
/*
* read a firmware binary
*/
if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_header.header_size = sizeof(payload_header);
payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */
memcpy(new_data, &payload_header, sizeof(payload_header));
memcpy(new_data + sizeof(payload_header), data, bin_size);
buf = new_data;
bin_size += sizeof(payload_header);
}
/* first, calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = buf;
auth_context.image_size = bin_size;
if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
buf_list[i] = data_list[i];
/* insert fmp payload header right before the payload */
if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i]));
if (!new_data_list[i])
goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i],
bin_sizes[i]);
buf_list[i] = new_data_list[i];
bin_sizes[i] += sizeof(payload_headers[i]); }
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
/* calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_contexts[i].key_file = privkey_file;
auth_contexts[i].cert_file = cert_file;
auth_contexts[i].auth.monotonic_count = mcounts[i];
auth_contexts[i].image_data = buf_list[i];
auth_contexts[i].image_size = bin_sizes[i];
if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
}
if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
} } }
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
+ sizeof(capsule) + sizeof(uint64_t)
+ sizeof(image)
+ bin_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
+ sizeof(capsule)
+ size * sizeof(uint64_t); /* size of item_offset_list */
for (int i = 0; i < size; i++) {
offsets[i] = header.capsule_image_size - sizeof(header);
header.capsule_image_size += sizeof(images[i])
+ bin_sizes[i];
if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
} if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err; /* * firmware capsule header
* This capsule has only one firmware capsule image. */ capsule.version = 0x00000001; capsule.embedded_driver_count = 0;
capsule.payload_item_count = 1;
capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
offset = sizeof(capsule) + sizeof(uint64_t);
if (write_capsule_file(f, &offset, sizeof(offset),
"Offset to capsule image"))
if (write_capsule_file(f, &offsets, size * sizeof(uint64_t),
"Offsets to capsule images")) goto err;
/*
* firmware capsule image header
*/
image.version = 0x00000003;
memcpy(&image.update_image_type_id, guid, sizeof(*guid));
image.update_image_index = index;
image.reserved[0] = 0;
image.reserved[1] = 0;
image.reserved[2] = 0;
image.update_image_size = bin_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */
image.update_hardware_instance = instance;
image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &image, sizeof(image),
"Firmware capsule image header"))
goto err;
/*
* signature
*/
if (auth_context.sig_size) {
if (write_capsule_file(f, &auth_context.auth,
sizeof(auth_context.auth),
"Authentication header"))
for (int i = 0; i < size; i++) {
/*
* firmware capsule image header
*/
images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i]));
images[i].update_image_index = indices[i];
images[i].reserved[0] = 0;
images[i].reserved[1] = 0;
images[i].reserved[2] = 0;
images[i].update_image_size = bin_sizes[i];
if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
images[i].update_vendor_code_size = 0; /* none */
images[i].update_hardware_instance = instances[i];
images[i].image_capsule_support = 0;
if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
if (write_capsule_file(f, auth_context.sig_data,
auth_context.sig_size, "Signature"))
/*
* signature
*/
if (auth_contexts[i].sig_size) {
if (write_capsule_file(f, &auth_contexts[i].auth,
sizeof(auth_contexts[i].auth),
"Authentication header"))
goto err;
if (write_capsule_file(f, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, "Signature"))
goto err;
}
/*
* firmware binary
*/
if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err; }
/*
* firmware binary
*/
if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
goto err;
ret = 0;
err: if (f) fclose(f);
free_sig_data(&auth_context);
free(data);
free(new_data);
for (int i = 0; i < size; i++) {
free_sig_data(&auth_contexts[i]);
free(data_list[i]);
free(new_data_list[i]);
} return ret;
} @@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{
const char *c;
int count = 0;
if (!*list)
return 0;
for (c = list; *c; c++) {
if (*c == separator)
count++;
}
/* correct count if no trailing separator present */
if (*(c - 1) != separator)
count++;
return count;
+}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{
int current_count = count_items(list, separator);
if (*count < 0)
*count = current_count;
if (*count == 0 ||
*count != current_count)
return -1;
return 0;
+}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{
const char separator_str[] = {separator, '\0'};
char *end;
for (int i = 0; i < size; i++) {
elements[i] = strsep(&list, separator_str);
if (!elements[i])
return -1;
}
end = strsep(&list, separator_str); /* NULL or empty string expected */
if (end && *end)
return -1;
return 0;
+}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{
void *array;
array = malloc(count * obj_size);
if (!array)
fprintf(stderr, "Could not allocate memory for %s\n", name);
return array;
+}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list contains the UUIDs
- to convert and store in the array. Upon failure an error message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size,
const char *name)
+{
efi_guid_t *guids;
guids = alloc_array(size, sizeof(efi_guid_t), name);
if (!guids)
return NULL;
for (int i = 0; i < size; i++) {
if (uuid_parse(elements[i], (unsigned char *)(guids + i))) {
fprintf(stderr, "Wrong %s format\n", name);
free(guids);
return NULL;
}
convert_uuid_to_guid((unsigned char *)(guids + i));
}
return guids;
+}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size,
const char *name)
+{
unsigned long *array;
array = alloc_array(size, sizeof(unsigned long), name);
if (!array)
return NULL;
for (int i = 0; i < size; i++)
array[i] = strtoul(elements[i], NULL, 0);
return array;
+}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator,
bool multiple_times, char *name)
+{
char **elements;
if (multiple_times) {
fprintf(stderr, "%s specified multiple times\n", name);
return NULL;
}
if (update_itemcount(listcount, list, separator)) {
fprintf(stderr, "List count not consistent with previous or list not provided\n");
return NULL;
}
elements = alloc_array(*listcount, sizeof(char *), name);
if (!elements)
return NULL;
if (split_list(elements, *listcount, list, separator)) {
fprintf(stderr, "Could not parse %s list\n", name);
free(elements);
return NULL;
}
return elements;
+}
/**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) {
efi_guid_t *guid;
unsigned char uuid_buf[16];
unsigned long index, instance;
uint64_t mcount;
const char separator = ',';
const efi_guid_t *guids; /* an array */
const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags;
const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file;
int c, idx;
struct fmp_payload_header_params fmp_ph_params = { 0 };
int listcount, c, idx;
guid = NULL;
index = 0;
instance = 0;
mcount = 0;
guids = NULL;
indices = NULL;
instances = NULL;
mcounts = NULL;
oemflags = 0;
blob_paths = NULL; privkey_file = NULL; cert_file = NULL;
elements = NULL;
listcount = -1;
fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g':
if (guid) {
fprintf(stderr,
"Image type already specified\n");
elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
"GUID");
if (!elements) exit(EXIT_FAILURE);
}
if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
guids = init_guids(elements, listcount, "GUID");
if (!guids) exit(EXIT_FAILURE);
}
convert_uuid_to_guid(uuid_buf);
guid = (efi_guid_t *)uuid_buf;
free(elements);
elements = NULL; break; case 'i':
index = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!indices, "index");
if (!elements)
exit(EXIT_FAILURE);
indices = init_uls(elements, listcount, "index");
if (!indices)
exit(EXIT_FAILURE);
free(elements);
elements = NULL;
break;
case 'b':
blob_paths = (const char **)init_list(&listcount, optarg, separator,
!!blob_paths, "blob path");
if (!blob_paths)
exit(EXIT_FAILURE); break; case 'I':
instance = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!instances, "instance");
if (!elements)
exit(EXIT_FAILURE);
instances = init_uls(elements, listcount, "instance");
if (!instances)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'v':
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
fmp_ph_params.have_header = true;
elements = (const char **)init_list(&listcount, optarg, separator,
!!fw_versions, "firmware version");
if (!elements)
exit(EXIT_FAILURE);
fw_versions = init_uls(elements, listcount, "firmware version");
if (!fw_versions)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'p': if (privkey_file) {
@@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm':
mcount = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!mcounts, "monotonic count");
if (!elements)
exit(EXIT_FAILURE);
mcounts = init_uls(elements, listcount, "monotonic count");
if (!mcounts)
exit(EXIT_FAILURE);
free(elements);
elements = NULL; break; case 'd': dump_sig = 1;
@@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB &&
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guid) ||
((capsule_type == CAPSULE_REVERT) && guid)))) {
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guids) ||
((capsule_type == CAPSULE_ACCEPT) && listcount != 1) ||
((capsule_type == CAPSULE_REVERT) && guids)))) { print_usage(); exit(EXIT_FAILURE); }
/* populate blob_paths if image blob was provided as positional argument */
if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) {
blob_paths = malloc(sizeof(char *));
if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n");
exit(EXIT_FAILURE);
}
*blob_paths = argv[argc - 2];
}
/* populate arrays with zeros if they are not provided */
if (!indices)
indices = calloc(listcount, sizeof(unsigned long));
if (!instances)
instances = calloc(listcount, sizeof(unsigned long));
if (!mcounts)
mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) {
if (create_empty_capsule(argv[argc - 1], guid,
if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); }
} else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) {
} else if (create_fwbin(argv[argc - 1], blob_paths, guids,
indices, instances, fw_versions,
mcounts, listcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.30.2

On 6/16/23 13:34, Stefan Herbrechtsmeier wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
The UEFI [1] specification allows multiple payloads inside the capsule body. Add support for this. The command line arguments are kept backwards-compatible.
[1] https://uefi.org/specs/UEFI/2.10/index.html
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
tools/eficapsule.h | 5 - tools/mkeficapsule.c | 636 ++++++++++++++++++++++++++++++++----------- 2 files changed, 475 insertions(+), 166 deletions(-)
diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 753fb73313..001af3217c 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -138,9 +138,4 @@ struct fmp_payload_header { uint32_t lowest_supported_version; };
-struct fmp_payload_header_params {
- bool have_header;
- uint32_t fw_version;
-};
- #endif /* _EFI_CAPSULE_H */
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b8db00b16b..1a4de0f092 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ 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;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; +static const char *opts_short = "g:i:b:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -40,6 +40,7 @@ enum { static struct option options[] = { {"guid", required_argument, NULL, 'g'}, {"index", required_argument, NULL, 'i'},
- {"image_blob", required_argument, NULL, 'b'}, {"instance", required_argument, NULL, 'I'}, {"fw-version", required_argument, NULL, 'v'}, {"private-key", required_argument, NULL, 'p'},
@@ -55,21 +56,22 @@ static struct option options[] = {
static void print_usage(void) {
- fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
- fprintf(stderr, "Usage: %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"
"\t-I, --instance <instance> update hardware instance\n"
"\t-v, --fw-version <version> firmware version\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n"
A later patch in the series mentions the lowest supported version. Why can't we set it?
Best regards
Heinrich
"\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-h, --help print a help message\n",
"\t-g, --guid <guid list> comma-separated list of guids for image blob types\n"
"\t-i, --index <index list> comma-separated list of update image indices\n"
"\t-b, --image_blob <blob list> comma-separated list of image blobs\n"
"\t-I, --instance <instance list> comma-separated list of update hardware instances\n"
"\t-v, --fw-version <version list> comma-separated list of firmware versions\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <monotonic-count list> comma-separated list of monotonic counts\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\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"
tool_name); }"\t-h, --help print a help message\n",
@@ -336,16 +338,18 @@ static int create_auth_data(struct auth_context *ctx)
- @path: Path to a capsule file
- @signature: Signature data
- @sig_size: Size of signature data
- @index: The payload index the signature belongs to
- Signature data pointed to by @signature will be saved into
- a file whose file name is @path with ".p7" suffix.
- a file whose file name is @path with "_<index>.p7" suffix.
*/ static int dump_signature(const char *path, const uint8_t *signature,
- If index is negative the suffix is ".p7" (for backwards compatibility).
- Return:
- 0 - on success
- -1 - on failure
size_t sig_size)
{ char *sig_path; FILE *f;size_t sig_size, int index)
@@ -356,7 +360,11 @@ static int dump_signature(const char *path, const uint8_t *signature, if (!sig_path) return ret;
- sprintf(sig_path, "%s.p7", path);
- if (index < 0)
sprintf(sig_path, "%s.p7", path);
- else
sprintf(sig_path, "%s_%d.p7", path, index);
- f = fopen(sig_path, "w"); if (!f) goto err;
@@ -386,14 +394,15 @@ static void free_sig_data(struct auth_context *ctx) /**
- create_fwbin - create an uefi capsule file
- @path: Path to a created capsule file
- @bin: Path to a firmware binary to encapsulate
- @guid: GUID of related FMP driver
- @index: Index number in capsule
- @bins: Paths to firmware binaries to encapsulate, an array
- @guids: GUIDs of related FMP drivers, an array
- @indices: Index numbers in capsule, an array
- @instance: Instance number in capsule
- @mcount: Monotonic count in authentication information
- @size: Size of the arrays
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -404,78 +413,87 @@ static void free_sig_data(struct auth_context *ctx)
- 0 - on success
- -1 - on failure
*/ -static int create_fwbin(const char *path, const char *bin,
const efi_guid_t *guid, unsigned long index,
unsigned long instance,
const struct fmp_payload_header_params *fmp_ph_params,
uint64_t mcount,
const char *privkey_file, const char *cert_file,
uint16_t oemflags)
+static int create_fwbin(const char *path, const char **bins,
const efi_guid_t *guids, const unsigned long *indices,
const unsigned long *instances,
const unsigned long *fw_versions, const unsigned long *mcounts,
int size, const char *privkey_file,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule;const char *cert_file, uint16_t oemflags)
- struct efi_firmware_management_capsule_image_header image;
- struct auth_context auth_context;
- struct efi_firmware_management_capsule_image_header images[size];
- struct auth_context auth_contexts[size]; FILE *f;
- uint8_t *data, *new_data, *buf;
- off_t bin_size;
- uint64_t offset;
- uint8_t *data_list[size], *new_data_list[size], *buf_list[size];
- off_t bin_sizes[size];
- uint64_t offsets[size]; int ret;
- struct fmp_payload_header payload_header;
struct fmp_payload_header payload_headers[size];
#ifdef DEBUG fprintf(stderr, "For output: %s\n", path);
- fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
- fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
- for (int i = 0; i < size; i++) {
fprintf(stderr, "\tpayload no: %d\n", i);
fprintf(stderr, "\t\tbin: %s\n\t\ttype: %pUl\n", bins[i], guids[i]);
fprintf(stderr, "\t\tindex: %lu\n\t\tinstance: %lu\n", indices[i], instances[i]);
- } #endif
auth_context.sig_size = 0; f = NULL;
data = NULL;
new_data = NULL; ret = -1;
/*
* read a firmware binary
*/
if (read_bin_file(bin, &data, &bin_size))
goto err;
- for (int i = 0; i < size; i++) {
auth_contexts[i].sig_size = 0;
data_list[i] = NULL;
new_data_list[i] = NULL;
- }
- buf = data;
- for (int i = 0; i < size; i++) {
int dump_index = (size == 1) ? -1 : i;
- /* insert fmp payload header right before the payload */
- if (fmp_ph_params->have_header) {
new_data = malloc(bin_size + sizeof(payload_header));
if (!new_data)
/*
* read a firmware binary
*/
if (read_bin_file(bins[i], &data_list[i], &bin_sizes[i])) goto err;
payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_header.header_size = sizeof(payload_header);
payload_header.fw_version = fmp_ph_params->fw_version;
payload_header.lowest_supported_version = 0; /* not used */
memcpy(new_data, &payload_header, sizeof(payload_header));
memcpy(new_data + sizeof(payload_header), data, bin_size);
buf = new_data;
bin_size += sizeof(payload_header);
- }
- /* first, calculate signature to determine its size */
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = buf;
auth_context.image_size = bin_size;
if (create_auth_data(&auth_context)) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
buf_list[i] = data_list[i];
/* insert fmp payload header right before the payload */
if (fw_versions) {
new_data_list[i] = malloc(bin_sizes[i] + sizeof(payload_headers[i]));
if (!new_data_list[i])
goto err;
payload_headers[i].signature = FMP_PAYLOAD_HDR_SIGNATURE;
payload_headers[i].header_size = sizeof(payload_headers[i]);
payload_headers[i].fw_version = fw_versions[i];
payload_headers[i].lowest_supported_version = 0; /* not used */
memcpy(new_data_list[i], (payload_headers + i), sizeof(payload_headers[i]));
memcpy(new_data_list[i] + sizeof(payload_headers[i]), data_list[i],
bin_sizes[i]);
buf_list[i] = new_data_list[i];
}bin_sizes[i] += sizeof(payload_headers[i]);
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
/* calculate signature to determine its size */
if (privkey_file && cert_file) {
auth_contexts[i].key_file = privkey_file;
auth_contexts[i].cert_file = cert_file;
auth_contexts[i].auth.monotonic_count = mcounts[i];
auth_contexts[i].image_data = buf_list[i];
auth_contexts[i].image_size = bin_sizes[i];
if (create_auth_data(&auth_contexts[i])) {
fprintf(stderr, "Signing firmware image failed\n");
goto err;
}
if (dump_sig &&
dump_signature(path, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, dump_index)) {
fprintf(stderr, "Creating signature file failed\n");
goto err;
} }}
@@ -498,81 +516,87 @@ static int create_fwbin(const char *path, const char *bin, if (oemflags) header.flags |= oemflags; header.capsule_image_size = sizeof(header)
+ sizeof(capsule) + sizeof(uint64_t)
+ sizeof(image)
+ bin_size;
- if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
+ sizeof(capsule)
+ size * sizeof(uint64_t); /* size of item_offset_list */
for (int i = 0; i < size; i++) {
offsets[i] = header.capsule_image_size - sizeof(header);
header.capsule_image_size += sizeof(images[i])
+ bin_sizes[i];
if (auth_contexts[i].sig_size)
header.capsule_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
} if (write_capsule_file(f, &header, sizeof(header), "Capsule header")) goto err;
/*
- firmware capsule header
*/ capsule.version = 0x00000001; capsule.embedded_driver_count = 0;* This capsule has only one firmware capsule image.
- capsule.payload_item_count = 1;
- capsule.payload_item_count = size; if (write_capsule_file(f, &capsule, sizeof(capsule), "Firmware capsule header")) goto err;
- offset = sizeof(capsule) + sizeof(uint64_t);
- if (write_capsule_file(f, &offset, sizeof(offset),
"Offset to capsule image"))
- if (write_capsule_file(f, &offsets, size * sizeof(uint64_t),
goto err;"Offsets to capsule images"))
- /*
* firmware capsule image header
*/
- image.version = 0x00000003;
- memcpy(&image.update_image_type_id, guid, sizeof(*guid));
- image.update_image_index = index;
- image.reserved[0] = 0;
- image.reserved[1] = 0;
- image.reserved[2] = 0;
- image.update_image_size = bin_size;
- if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
- image.update_vendor_code_size = 0; /* none */
- image.update_hardware_instance = instance;
- image.image_capsule_support = 0;
- if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
- if (write_capsule_file(f, &image, sizeof(image),
"Firmware capsule image header"))
goto err;
- /*
* signature
*/
- if (auth_context.sig_size) {
if (write_capsule_file(f, &auth_context.auth,
sizeof(auth_context.auth),
"Authentication header"))
- for (int i = 0; i < size; i++) {
/*
* firmware capsule image header
*/
images[i].version = 0x00000003;
memcpy(&images[i].update_image_type_id, &guids[i], sizeof(guids[i]));
images[i].update_image_index = indices[i];
images[i].reserved[0] = 0;
images[i].reserved[1] = 0;
images[i].reserved[2] = 0;
images[i].update_image_size = bin_sizes[i];
if (auth_contexts[i].sig_size)
images[i].update_image_size += sizeof(auth_contexts[i].auth)
+ auth_contexts[i].sig_size;
images[i].update_vendor_code_size = 0; /* none */
images[i].update_hardware_instance = instances[i];
images[i].image_capsule_support = 0;
if (auth_contexts[i].sig_size)
images[i].image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
if (write_capsule_file(f, &images[i], sizeof(images[i]),
"Firmware capsule image header")) goto err;
if (write_capsule_file(f, auth_context.sig_data,
auth_context.sig_size, "Signature"))
/*
* signature
*/
if (auth_contexts[i].sig_size) {
if (write_capsule_file(f, &auth_contexts[i].auth,
sizeof(auth_contexts[i].auth),
"Authentication header"))
goto err;
if (write_capsule_file(f, auth_contexts[i].sig_data,
auth_contexts[i].sig_size, "Signature"))
goto err;
}
/*
* firmware binary
*/
}if (write_capsule_file(f, buf_list[i], bin_sizes[i], "Firmware binary")) goto err;
- /*
* firmware binary
*/
- if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
goto err;
- ret = 0; err: if (f) fclose(f);
- free_sig_data(&auth_context);
- free(data);
- free(new_data);
for (int i = 0; i < size; i++) {
free_sig_data(&auth_contexts[i]);
free(data_list[i]);
free(new_data_list[i]);
}
return ret; }
@@ -652,6 +676,228 @@ err: return ret; }
+/**
- count_items - count number of items in list
- @list: Pointer to a string
- @separator: Separator used to separate list items
- Count the number of items in a list. The list items
- are separated by a separator character inside the string.
- Trailing white spaces are not allowed except if it is the separator.
- Return:
- The item count.
- */
+int count_items(const char *list, char separator) +{
- const char *c;
- int count = 0;
- if (!*list)
return 0;
- for (c = list; *c; c++) {
if (*c == separator)
count++;
- }
- /* correct count if no trailing separator present */
- if (*(c - 1) != separator)
count++;
- return count;
+}
+/**
- update_itemcount - update item count
- @count: The count to be updated
- @list: The item list
- @separator: List separator
- Initialize the count if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Return:
- 0 - on success
- -1 - if a check fails
- */
+int update_itemcount(int *count, const char *list, char separator) +{
- int current_count = count_items(list, separator);
- if (*count < 0)
*count = current_count;
- if (*count == 0 ||
*count != current_count)
return -1;
- return 0;
+}
+/**
- split_list - split list into elements
- @elements: Pointer to string array
- @size: The array size
- @list: The item list
- @separator: List separator
- Split a comma-separated list into its elements.
- Return:
- 0 - on success
- -1 - on failure
- */
+int split_list(char **elements, int size, char *list, char separator) +{
- const char separator_str[] = {separator, '\0'};
- char *end;
- for (int i = 0; i < size; i++) {
elements[i] = strsep(&list, separator_str);
if (!elements[i])
return -1;
- }
- end = strsep(&list, separator_str); /* NULL or empty string expected */
- if (end && *end)
return -1;
- return 0;
+}
+/**
- alloc_array - allocate memory for array
- @count: The number of elements
- @obj_size: The size of a single element
- @name: The name of the array
- This is a wrapper for malloc which prints an error
- message on failure.
- Return:
- Pointer to the allocated memory on success
- NULL on failure
- */
+void *alloc_array(unsigned int count, size_t obj_size, const char *name) +{
- void *array;
- array = malloc(count * obj_size);
- if (!array)
fprintf(stderr, "Could not allocate memory for %s\n", name);
- return array;
+}
+/**
- init_guids - populate guid array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of guid structs. The list contains the UUIDs
- to convert and store in the array. Upon failure an error message is
- printed.
- Return:
- The initialized GUID array on success
- NULL on failure
- */
+efi_guid_t *init_guids(const char **elements, unsigned int size,
const char *name)
+{
- efi_guid_t *guids;
- guids = alloc_array(size, sizeof(efi_guid_t), name);
- if (!guids)
return NULL;
- for (int i = 0; i < size; i++) {
if (uuid_parse(elements[i], (unsigned char *)(guids + i))) {
fprintf(stderr, "Wrong %s format\n", name);
free(guids);
return NULL;
}
convert_uuid_to_guid((unsigned char *)(guids + i));
- }
- return guids;
+}
+/**
- init_uls - populate unsigned long array
- @elements: String array of elements to be converted
- @size: The array size
- @name: The name of the array
- Allocate and populate an array of unsgined longs. Upon failure an
- error message is printed.
- Return:
- The initialized array on success
- NULL on failure
- */
+unsigned long *init_uls(const char **elements, unsigned int size,
const char *name)
+{
- unsigned long *array;
- array = alloc_array(size, sizeof(unsigned long), name);
- if (!array)
return NULL;
- for (int i = 0; i < size; i++)
array[i] = strtoul(elements[i], NULL, 0);
- return array;
+}
+/**
- init_list - parse list and allocate elements
- @listcount: The list count to be checked and updated
- @list: The list to be parsed
- @separator: The list separator
- @name: The name of the list
- @multiple_times: List encountered multiple times
- Routine for command line argument lists.
- Parse the string list and count the list elements.
- Initialize the listcount if it is uninitialized (negative value).
- Check that the list contains at least one item.
- Check if an already initialized count is consistent with the list count.
- Allocate the string array and populate it with the list elements.
- The array should be freed in the calling function.
- Upon failure an error message is printed and the program exits.
- Return:
- The initialized array on success
- NULL on failure
- */
+char **init_list(int *listcount, char *list, char separator,
bool multiple_times, char *name)
+{
- char **elements;
- if (multiple_times) {
fprintf(stderr, "%s specified multiple times\n", name);
return NULL;
- }
- if (update_itemcount(listcount, list, separator)) {
fprintf(stderr, "List count not consistent with previous or list not provided\n");
return NULL;
- }
- elements = alloc_array(*listcount, sizeof(char *), name);
- if (!elements)
return NULL;
- if (split_list(elements, *listcount, list, separator)) {
fprintf(stderr, "Could not parse %s list\n", name);
free(elements);
return NULL;
- }
- return elements;
+}
- /**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -666,24 +912,27 @@ err: */ int main(int argc, char **argv) {
- efi_guid_t *guid;
- unsigned char uuid_buf[16];
- unsigned long index, instance;
- uint64_t mcount;
- const char separator = ',';
- const efi_guid_t *guids; /* an array */
- const unsigned long *indices, *instances, *mcounts, *fw_versions; /* arrays */ unsigned long oemflags;
- const char **blob_paths, **elements; /* string arrays */ const char *privkey_file, *cert_file;
- int c, idx;
- struct fmp_payload_header_params fmp_ph_params = { 0 };
- int listcount, c, idx;
- guid = NULL;
- index = 0;
- instance = 0;
- mcount = 0;
- guids = NULL;
- indices = NULL;
- instances = NULL;
- mcounts = NULL;
- oemflags = 0;
- blob_paths = NULL; privkey_file = NULL; cert_file = NULL;
- elements = NULL;
- listcount = -1;
- fw_versions = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
- oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -691,27 +940,62 @@ int main(int argc, char **argv)
switch (c) { case 'g':
if (guid) {
fprintf(stderr,
"Image type already specified\n");
elements = (const char **)init_list(&listcount, optarg, separator, !!guids,
"GUID");
if (!elements) exit(EXIT_FAILURE);
}
if (uuid_parse(optarg, uuid_buf)) {
fprintf(stderr, "Wrong guid format\n");
guids = init_guids(elements, listcount, "GUID");
if (!guids) exit(EXIT_FAILURE);
}
convert_uuid_to_guid(uuid_buf);
guid = (efi_guid_t *)uuid_buf;
free(elements);
case 'i':elements = NULL; break;
index = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!indices, "index");
if (!elements)
exit(EXIT_FAILURE);
indices = init_uls(elements, listcount, "index");
if (!indices)
exit(EXIT_FAILURE);
free(elements);
elements = NULL;
break;
case 'b':
blob_paths = (const char **)init_list(&listcount, optarg, separator,
!!blob_paths, "blob path");
if (!blob_paths)
case 'I':exit(EXIT_FAILURE); break;
instance = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!instances, "instance");
if (!elements)
exit(EXIT_FAILURE);
instances = init_uls(elements, listcount, "instance");
if (!instances)
exit(EXIT_FAILURE);
free(elements);
case 'v':elements = NULL; break;
fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
fmp_ph_params.have_header = true;
elements = (const char **)init_list(&listcount, optarg, separator,
!!fw_versions, "firmware version");
if (!elements)
exit(EXIT_FAILURE);
fw_versions = init_uls(elements, listcount, "firmware version");
if (!fw_versions)
exit(EXIT_FAILURE);
free(elements);
case 'p': if (privkey_file) {elements = NULL; break;
@@ -730,7 +1014,17 @@ int main(int argc, char **argv) cert_file = optarg; break; case 'm':
mcount = strtoul(optarg, NULL, 0);
elements = (const char **)init_list(&listcount, optarg, separator,
!!mcounts, "monotonic count");
if (!elements)
exit(EXIT_FAILURE);
mcounts = init_uls(elements, listcount, "monotonic count");
if (!mcounts)
exit(EXIT_FAILURE);
free(elements);
case 'd': dump_sig = 1;elements = NULL; break;
@@ -767,26 +1061,46 @@ int main(int argc, char **argv)
/* check necessary parameters */ if ((capsule_type == CAPSULE_NORMAL_BLOB &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(!((argc != optind + 2) ^ !(blob_paths && argc == optind + 1)) || !guids ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) || (capsule_type != CAPSULE_NORMAL_BLOB &&
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guid) ||
((capsule_type == CAPSULE_REVERT) && guid)))) {
((argc != optind + 1) ||
((capsule_type == CAPSULE_ACCEPT) && !guids) ||
((capsule_type == CAPSULE_ACCEPT) && listcount != 1) ||
((capsule_type == CAPSULE_REVERT) && guids)))) {
print_usage(); exit(EXIT_FAILURE); }
/* populate blob_paths if image blob was provided as positional argument */
if (capsule_type == CAPSULE_NORMAL_BLOB && !blob_paths) {
blob_paths = malloc(sizeof(char *));
if (!blob_paths) {
fprintf(stderr, "Could not allocate memory for blob paths\n");
exit(EXIT_FAILURE);
}
*blob_paths = argv[argc - 2];
}
/* populate arrays with zeros if they are not provided */
if (!indices)
indices = calloc(listcount, sizeof(unsigned long));
if (!instances)
instances = calloc(listcount, sizeof(unsigned long));
if (!mcounts)
mcounts = calloc(listcount, sizeof(uint64_t));
if (capsule_type != CAPSULE_NORMAL_BLOB) {
if (create_empty_capsule(argv[argc - 1], guid,
}if (create_empty_capsule(argv[argc - 1], guids, capsule_type == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE);
- } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, &fmp_ph_params, mcount, privkey_file,
cert_file, (uint16_t)oemflags) < 0) {
- } else if (create_fwbin(argv[argc - 1], blob_paths, guids,
indices, instances, fw_versions,
mcounts, listcount, privkey_file,
fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }cert_file, (uint16_t)oemflags) < 0) {

From: Malte Schmidt malte.schmidt@weidmueller.com
Test updating U-Boot and the U-Boot environment with a single capsule. This test also checks that the mkeficapsule tool builds a capsule containing both images to update correctly. Testing of a signed and an unsigned image is implemented.
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com ---
test/py/tests/test_efi_capsule/conftest.py | 18 +++++++- .../test_capsule_firmware_raw.py | 46 ++++++++++++++++--- .../test_capsule_firmware_signed_raw.py | 24 ++++++++-- 3 files changed, 77 insertions(+), 11 deletions(-)
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index 054be1ee97..7acafb8599 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -124,6 +124,12 @@ def efi_capsule_data(request, u_boot_config): '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' % (data_dir, u_boot_config.build_dir), shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 1,2 ' + '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8,5A7021F5-FEF2-48B4-AABA-832E777418C0 ' + '-b u-boot.bin.new,u-boot.env.new ' + 'Test106' % + (data_dir, u_boot_config.build_dir), + shell=True)
if capsule_auth_enabled: # raw firmware signed with proper key @@ -205,7 +211,17 @@ def efi_capsule_data(request, u_boot_config): 'uboot_bin_env.itb Test115' % (data_dir, u_boot_config.build_dir), shell=True) - + # multiple raw firmwares with proper key + check_call('cd %s; %s/tools/mkeficapsule --index 1,2 ' + '--monotonic-count 1,2 ' + '--private-key SIGNER.key ' + '--certificate SIGNER.crt ' + '--fw-version 5,10 ' + '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8,5A7021F5-FEF2-48B4-AABA-832E777418C0 ' + '-b u-boot.bin.new,u-boot.env.new ' + 'Test116' + % (data_dir, u_boot_config.build_dir), + shell=True) # Create a disk image with EFI system partition check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' % (mnt_point, image_path), shell=True) diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py index 80d791e3de..627b93b337 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py @@ -105,12 +105,27 @@ class TestEfiCapsuleFirmwareRaw: def test_efi_capsule_fw3( self, u_boot_config, u_boot_console, efi_capsule_data): """ Test Case 3 - Update U-Boot on SPI Flash, raw image format + Update U-Boot and U-Boot environment, raw image format, two separate capsules + 0x100000-0x150000: U-Boot binary (but dummy) + """ + self.efi_capsule_fw3_common(u_boot_config, u_boot_console, efi_capsule_data, "3", ['Test01', 'Test02']) + + def test_efi_capsule_fw3_multi( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ Test Case 3-multi + Update U-Boot and U-Boot environment, raw image format, one capsule + 0x100000-0x150000: U-Boot binary (but dummy) + """ + self.efi_capsule_fw3_common(u_boot_config, u_boot_console, efi_capsule_data, "3-multi", ['TODO!']) + + def efi_capsule_fw3_common(self, u_boot_config, u_boot_console, efi_capsule_data, test_case_no, capsule_files): + """ Test Case + Update U-Boot and U-Boot environment, raw image format 0x100000-0x150000: U-Boot binary (but dummy) """ disk_img = efi_capsule_data - capsule_files = ['Test01', 'Test02'] - with u_boot_console.log.section('Test Case 3-a, before reboot'): + + with u_boot_console.log.section('Test Case %s-a, before reboot' % test_case_no): setup(u_boot_console, disk_img, '0x0000000000000004') init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old') init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old') @@ -124,7 +139,7 @@ class TestEfiCapsuleFirmwareRaw: # reboot u_boot_console.restart_uboot(expect_reset = capsule_early)
- with u_boot_console.log.section('Test Case 3-b, after reboot'): + with u_boot_console.log.section('Test Case %s-b, after reboot' % test_case_no): if not capsule_early: exec_manual_update(u_boot_console, disk_img, capsule_files)
@@ -147,6 +162,7 @@ class TestEfiCapsuleFirmwareRaw: expected = 'u-boot-env:Old' if capsule_auth else 'u-boot-env:New' verify_content(u_boot_console, '150000', expected)
+ def test_efi_capsule_fw4( self, u_boot_config, u_boot_console, efi_capsule_data): """ Test Case 4 @@ -154,9 +170,25 @@ class TestEfiCapsuleFirmwareRaw: 0x100000-0x150000: U-Boot binary (but dummy) 0x150000-0x200000: U-Boot environment (but dummy) """ + self.efi_capsule_fw4_common(u_boot_config, u_boot_console, efi_capsule_data, '4', ['Test101', 'Test102']) + + def test_efi_capsule_fw4_multi( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ Test Case 4-multi + Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version, one capsule + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) + """ + self.efi_capsule_fw4_common(u_boot_config, u_boot_console, efi_capsule_data, '4-multi', ['Test106']) + + def efi_capsule_fw4_common(self, u_boot_config, u_boot_console, efi_capsule_data, test_case_no, capsule_files): + """ Test Case + Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) + """ disk_img = efi_capsule_data - capsule_files = ['Test101', 'Test102'] - with u_boot_console.log.section('Test Case 4-a, before reboot'): + with u_boot_console.log.section('Test Case %s-a, before reboot' % test_case_no): setup(u_boot_console, disk_img, '0x0000000000000004') init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old') init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old') @@ -169,7 +201,7 @@ class TestEfiCapsuleFirmwareRaw: 'config_efi_capsule_on_disk_early') capsule_auth = u_boot_config.buildconfig.get( 'config_efi_capsule_authenticate') - with u_boot_console.log.section('Test Case 4-b, after reboot'): + with u_boot_console.log.section('Test Case %s-b, after reboot' % test_case_no): if not capsule_early: exec_manual_update(u_boot_console, disk_img, capsule_files)
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py index ad2b1c6324..1624311f00 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py @@ -125,12 +125,30 @@ class TestEfiCapsuleFirmwareSignedRaw(): """Test Case 4 - Update U-Boot on SPI Flash, raw image format with version information 0x100000-0x150000: U-Boot binary (but dummy)
+ If the capsule is properly signed, the authentication + should pass and the firmware be updated. + """ + self.efi_capsule_auth4_common(u_boot_config, u_boot_console, efi_capsule_data, '4', ['Test111', 'Test112']) + + def test_efi_capsule_auth4_multi( + self, u_boot_config, u_boot_console, efi_capsule_data): + """Test Case 4-multi - Update U-Boot on SPI Flash, raw image format with version information, one capsule + 0x100000-0x150000: U-Boot binary (but dummy) + + If the capsule is properly signed, the authentication + should pass and the firmware be updated. + """ + self.efi_capsule_auth4_common(u_boot_config, u_boot_console, efi_capsule_data, '4-multi', ['Test116']) + + def efi_capsule_auth4_common(self, u_boot_config, u_boot_console, efi_capsule_data, test_case_no, capsule_files): + """Test Case - Update U-Boot on SPI Flash, raw image format with version information + 0x100000-0x150000: U-Boot binary (but dummy) + If the capsule is properly signed, the authentication should pass and the firmware be updated. """ disk_img = efi_capsule_data - capsule_files = ['Test111', 'Test112'] - with u_boot_console.log.section('Test Case 4-a, before reboot'): + with u_boot_console.log.section('Test Case %s-a, before reboot' % test_case_no): setup(u_boot_console, disk_img, '0x0000000000000004') init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old') place_capsule_file(u_boot_console, capsule_files) @@ -139,7 +157,7 @@ class TestEfiCapsuleFirmwareSignedRaw():
capsule_early = u_boot_config.buildconfig.get( 'config_efi_capsule_on_disk_early') - with u_boot_console.log.section('Test Case 4-b, after reboot'): + with u_boot_console.log.section('Test Case %s-b, after reboot' % test_case_no): if not capsule_early: exec_manual_update(u_boot_console, disk_img, capsule_files)

From: Malte Schmidt malte.schmidt@weidmueller.com
mkeficapsule now supports multiple blobs. Update the documentation accordingly. Although the image blob can still be specified as positional parameter for backwards compatibility, remove it from the documentation to discourage its usage.
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com ---
doc/develop/uefi/uefi.rst | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 6626ceec52..b513934d31 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -304,6 +304,7 @@ Creating a capsule file ***********************
A capsule file can be created by using tools/mkeficapsule. +A capsule file can contain multiple images to update. To build this tool, enable::
CONFIG_TOOLS_MKEFICAPSULE=y @@ -314,10 +315,14 @@ Run the following command .. code-block:: console
$ mkeficapsule \ - --index <index> --instance 0 \ - --guid <image GUID> \ + --index <index list> \ + --instance <instance list> \ + --guid <image GUID list> \ + --image_blob <image_blob list> \ <capsule_file_name>
+The list entries must be comma-separated. + The UEFI specification does not define the firmware versioning mechanism. EDK II reference implementation inserts the FMP Payload Header right before the payload. It coutains the fw_version and lowest supported version, @@ -337,14 +342,16 @@ add --fw-version option in mkeficapsule tool. .. code-block:: console
$ mkeficapsule \ - --index <index> --instance 0 \ - --guid <image GUID> \ - --fw-version 5 \ + --index <index list> \ + --instance <instance list> \ + --guid <image GUID list> \ + --fw-version <version list> \ <capsule_file_name>
If the --fw-version option is not set, FMP Payload Header is not inserted and fw_version is set as 0.
+ Performing the update *********************
@@ -465,9 +472,11 @@ following command can be issued .. code-block:: bash
$ ./tools/mkeficapsule \ - --index 0x3 --instance 0 \ + --index 0x3 \ + --instance 0 \ --guid c1b629f1-ce0e-4894-82bf-f0a38387e630 \ - optee.bin optee.capsule + --image_blob optee.bin \ + optee.capsule
Enabling Capsule Authentication @@ -509,9 +518,11 @@ and used by the steps highlighted below. $ mkeficapsule --monotonic-count 1 \ --private-key CRT.key \ --certificate CRT.crt \ - --index 1 --instance 0 \ - [--fit | --raw | --guid <guid-string] \ - <image_blob> <capsule_file_name> + --index 1 \ + --instance 0 \ + [--fit | --raw | --guid <guid-string>] \ + --image_blob <image_blob> \ + <capsule_file_name>
4. Insert the signature list into a device tree in the following format::

On 6/16/23 13:34, Stefan Herbrechtsmeier wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
mkeficapsule now supports multiple blobs. Update the documentation accordingly. Although the image blob can still be specified as positional parameter for backwards compatibility, remove it from the documentation to discourage its usage.
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
doc/develop/uefi/uefi.rst | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 6626ceec52..b513934d31 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -304,6 +304,7 @@ Creating a capsule file
A capsule file can be created by using tools/mkeficapsule. +A capsule file can contain multiple images to update. To build this tool, enable::
CONFIG_TOOLS_MKEFICAPSULE=y
@@ -314,10 +315,14 @@ Run the following command .. code-block:: console
$ mkeficapsule \
--index <index> --instance 0 \
--guid <image GUID> \
--index <index list> \
--instance <instance list> \
--guid <image GUID list> \
--image_blob <image_blob list> \ <capsule_file_name>
+The list entries must be comma-separated.
- The UEFI specification does not define the firmware versioning mechanism. EDK II reference implementation inserts the FMP Payload Header right before
%s/EDK/The EDK/
the payload. It coutains the fw_version and lowest supported version,
%s/coutains/contains/ %s/fw_version/new firmware version/
Do I understand it correctly?
"... the lowest supported version which can be upgraded by the capsule."
@@ -337,14 +342,16 @@ add --fw-version option in mkeficapsule tool. .. code-block:: console
$ mkeficapsule \
--index <index> --instance 0 \
--guid <image GUID> \
--fw-version 5 \
--index <index list> \
--instance <instance list> \
--guid <image GUID list> \
--fw-version <version list> \ <capsule_file_name>
If the --fw-version option is not set, FMP Payload Header is not inserted and fw_version is set as 0.
%s/FMP/the FMP/
How do you set the lowest supported version which you mention above?
It remains unmentioned how the comma separated lists are related. Should they all have the same number of entries? Are they matched by index? Can an entry be left out to use the default, e.g.:
-fw-version 22,,33,44
Wouldn't it be preferable to provide a full command example?
Best regards
Heinrich
- Performing the update
@@ -465,9 +472,11 @@ following command can be issued .. code-block:: bash
$ ./tools/mkeficapsule \
--index 0x3 --instance 0 \
--index 0x3 \
--instance 0 \ --guid c1b629f1-ce0e-4894-82bf-f0a38387e630 \
optee.bin optee.capsule
--image_blob optee.bin \
optee.capsule
Enabling Capsule Authentication
@@ -509,9 +518,11 @@ and used by the steps highlighted below. $ mkeficapsule --monotonic-count 1 \ --private-key CRT.key \ --certificate CRT.crt \
--index 1 --instance 0 \
[--fit | --raw | --guid <guid-string] \
<image_blob> <capsule_file_name>
--index 1 \
--instance 0 \
[--fit | --raw | --guid <guid-string>] \
--image_blob <image_blob> \
<capsule_file_name>
- Insert the signature list into a device tree in the following format::

From: Malte Schmidt malte.schmidt@weidmueller.com
There seems to be some misused or inaccurate namings regarding the capsule concept. Set the naming straight and add a table showing the naming conventions. This table is based on the images found in chapter 23 of the UEFI 2.10 specifications [1]. The table should help to build a common understanding between the authors and readers of the documentation.
[1] https://uefi.org/specs/UEFI/2.10/index.html
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com
Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com ---
doc/develop/uefi/uefi.rst | 42 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index b513934d31..56188c5b10 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -300,6 +300,42 @@ not present are ignored when determining the active boot option. Please note that capsules will be applied in the alphabetic order of capsule file names.
+Structure of a capsule file +*************************** + +The strucutre of a firmware management capsule as defined in [1] is shown +below. The tools/mkeficapsule program supports creating firmware management +capsules with multiple payloads and optionally with firmware image +authentication. + +.. code-block:: text + + +-------------------------------------------------------------------------+ + | EFI_CAPSULE_HEADER | + +--------------+----------------------------------------------------------+ + | Capsule Body | EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER | + | +----------------------------------------------------------+ + | | Optional Driver 1 | + | +----------------------------------------------------------+ + | | Optional Driver 2 | + | +----------------------------------------------------------+ + | | ... | + | +-----------+----------------------------------------------+ + | | Payload 1 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER | + | | +----------------------------------------------+ + | | | Firmware Image Authentication (optional) | + | | +----------------------------------------------+ + | | | Dependency Expression (optional) | + | | +----------------------------------------------+ + | | | Firmware Image | + | +-----------+----------------------------------------------+ + | | Payload 2 | + | +----------------------------------------------------------+ + | | ... | + | +----------------------------------------------------------+ + | | Payload n | + +--------------+----------------------------------------------------------+ + Creating a capsule file ***********************
@@ -482,9 +518,9 @@ following command can be issued Enabling Capsule Authentication *******************************
-The UEFI specification defines a way of authenticating the capsule to -be updated by verifying the capsule signature. The capsule signature -is computed and prepended to the capsule payload at the time of +The UEFI specification defines a way of authenticating the capsule payload +to be updated by verifying the signature of each capsule payload. The payload +signature is computed and prepended to the capsule payload at the time of capsule generation. This signature is then verified by using the public key stored as part of the X509 certificate. This certificate is in the form of an efi signature list (esl) file, which is embedded in

On 6/16/23 13:34, Stefan Herbrechtsmeier wrote:
From: Malte Schmidt malte.schmidt@weidmueller.com
There seems to be some misused or inaccurate namings regarding the capsule concept. Set the naming straight and add a table showing the naming conventions. This table is based on the images found in chapter 23 of the UEFI 2.10 specifications [1]. The table should help to build a common understanding between the authors and readers of the documentation.
[1] https://uefi.org/specs/UEFI/2.10/index.html
Signed-off-by: Malte Schmidt malte.schmidt@weidmueller.com
Signed-off-by: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
doc/develop/uefi/uefi.rst | 42 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index b513934d31..56188c5b10 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -300,6 +300,42 @@ not present are ignored when determining the active boot option. Please note that capsules will be applied in the alphabetic order of capsule file names.
+Structure of a capsule file +***************************
+The strucutre of a firmware management capsule as defined in [1] is shown
%s/strucutre/structure/
Best regards
Heinrich
+below. The tools/mkeficapsule program supports creating firmware management +capsules with multiple payloads and optionally with firmware image +authentication.
+.. code-block:: text
- +-------------------------------------------------------------------------+
- | EFI_CAPSULE_HEADER |
- +--------------+----------------------------------------------------------+
- | Capsule Body | EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER |
- | +----------------------------------------------------------+
- | | Optional Driver 1 |
- | +----------------------------------------------------------+
- | | Optional Driver 2 |
- | +----------------------------------------------------------+
- | | ... |
- | +-----------+----------------------------------------------+
- | | Payload 1 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER |
- | | +----------------------------------------------+
- | | | Firmware Image Authentication (optional) |
- | | +----------------------------------------------+
- | | | Dependency Expression (optional) |
- | | +----------------------------------------------+
- | | | Firmware Image |
- | +-----------+----------------------------------------------+
- | | Payload 2 |
- | +----------------------------------------------------------+
- | | ... |
- | +----------------------------------------------------------+
- | | Payload n |
- +--------------+----------------------------------------------------------+
- Creating a capsule file
@@ -482,9 +518,9 @@ following command can be issued Enabling Capsule Authentication
-The UEFI specification defines a way of authenticating the capsule to -be updated by verifying the capsule signature. The capsule signature -is computed and prepended to the capsule payload at the time of +The UEFI specification defines a way of authenticating the capsule payload +to be updated by verifying the signature of each capsule payload. The payload +signature is computed and prepended to the capsule payload at the time of capsule generation. This signature is then verified by using the public key stored as part of the X509 certificate. This certificate is in the form of an efi signature list (esl) file, which is embedded in

On Fri, Jun 16, 2023 at 01:34:21PM +0200, Stefan Herbrechtsmeier wrote:
From: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
Currently, the mkeficapsule tool supports at most one payload inside the capsule. However, the UEFI specification and the u-boot code support multiple payloads inside one capsule. Extend the tool by this feature. The tool is kept backwards-compatible, so it can still be used and called exactly as before if desired.
One of reasons why only a single image be included is that I didn't see any strong reason to do so because U-Boot already has an archive format of multiple images, named FIT. I wanted to utilize the existing framework which is widely used on U-Boot and the current implementation of FMP supports this format. It seems to be just enough.
Do you have any useful use case of multiple images?
-Takahiro Akashi
Malte Schmidt (5): mkeficapsule: constify function parameters mkeficapsule: add support for multiple payloads inside capsule test: efi_capsule: test a capsule update containing multiple images doc: uefi: update mkeficapsule documentation doc: uefi: clarify capsule concept
doc/develop/uefi/uefi.rst | 73 +- test/py/tests/test_efi_capsule/conftest.py | 18 +- .../test_capsule_firmware_raw.py | 46 +- .../test_capsule_firmware_signed_raw.py | 24 +- tools/eficapsule.h | 5 - tools/mkeficapsule.c | 651 +++++++++++++----- 6 files changed, 622 insertions(+), 195 deletions(-)
-- 2.30.2

Hello Takahiro,
Am 17.06.2023 um 02:45 schrieb AKASHI Takahiro:
On Fri, Jun 16, 2023 at 01:34:21PM +0200, Stefan Herbrechtsmeier wrote:
From: Stefan Herbrechtsmeier stefan.herbrechtsmeier@weidmueller.com
Currently, the mkeficapsule tool supports at most one payload inside the capsule. However, the UEFI specification and the u-boot code support multiple payloads inside one capsule. Extend the tool by this feature. The tool is kept backwards-compatible, so it can still be used and called exactly as before if desired.
One of reasons why only a single image be included is that I didn't see any strong reason to do so because U-Boot already has an archive format of multiple images, named FIT. I wanted to utilize the existing framework which is widely used on U-Boot and the current implementation of FMP supports this format. It seems to be just enough.
Do you have any useful use case of multiple images?
-Takahiro Akashi
thank you for the feedback. I did not of think of this as the solution to our use case as I had my EFI view point glasses on. I will check if we can use the existing solution for our use case. However, I think the extension is either still valuable or some u-boot code could be removed.
Currently, the u-boot EFI code allows for multiple payloads as does the EFI specification. Are there any tests which test this part of the u-boot code? AFAIK my extension of the mkeficapsule tool is the first that allows for testing this u-boot code and does so with the accompanying tests.
Another question is: Why does the support for multiple payloads inside a capsule exists in the u-boot code basis in the first place? Right now there seem to be two ways to update multiple binaries with a capsule update (a FIT image and a capsule containing multiple payloads). Either it should be a valid assumption that both features can be used and the accompanying tools can be extended to help make use of them or documentation should exist which points out that only one of the solutions is acceptable.
Best Regards Malte
Malte Schmidt (5): mkeficapsule: constify function parameters mkeficapsule: add support for multiple payloads inside capsule test: efi_capsule: test a capsule update containing multiple images doc: uefi: update mkeficapsule documentation doc: uefi: clarify capsule concept
doc/develop/uefi/uefi.rst | 73 +- test/py/tests/test_efi_capsule/conftest.py | 18 +- .../test_capsule_firmware_raw.py | 46 +- .../test_capsule_firmware_signed_raw.py | 24 +- tools/eficapsule.h | 5 - tools/mkeficapsule.c | 651 +++++++++++++----- 6 files changed, 622 insertions(+), 195 deletions(-)
-- 2.30.2
participants (5)
-
AKASHI Takahiro
-
Heinrich Schuchardt
-
Schmidt, Malte
-
Stefan Herbrechtsmeier
-
Sughosh Ganu