[PATCH 2/5] mkeficapsule: add support for multiple payloads inside capsule

Stefan Herbrechtsmeier stefan.herbrechtsmeier-oss at weidmueller.com
Fri Jun 16 13:34:23 CEST 2023


From: Malte Schmidt <malte.schmidt at 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 at weidmueller.com>
Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier at 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



More information about the U-Boot mailing list