fiptool: Add support for operating on binary blobs using the UUID

Previously, fiptool only understood a fixed set of images as
specified in tbbr_config.c.  It preserved unknown images during
the update, unpack and remove operations but it was not possible to
explicitly refer to one of those unknown images.

Add a new --blob option to create/update/unpack/remove images that
are not known at compile time.  This is accomplished by specifying
the UUID and filename pair as shown below:

$ ./fiptool create --blob uuid=01234567-89ab-cdef-0123-456789abcdef,file=foo.bin fip.bin
$ ./fiptool info fip.bin
01234567-89ab-cdef-0123-456789abcdef: offset=0x60, size=0x1AA68

Fixes ARM-software/tf-issues#420

Change-Id: Iaac2504b9a4252289c09e73d29645cbe240f3a82
Signed-off-by: dp-arm <dimitris.papastamos@arm.com>
diff --git a/tools/fiptool/fiptool.c b/tools/fiptool/fiptool.c
index 70921ce..793519a 100644
--- a/tools/fiptool/fiptool.c
+++ b/tools/fiptool/fiptool.c
@@ -293,6 +293,42 @@
 	return NULL;
 }
 
+static void uuid_to_str(char *s, size_t len, const uuid_t *u)
+{
+	assert(len >= (_UUID_STR_LEN + 1));
+
+	snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
+	    u->time_low,
+	    u->time_mid,
+	    u->time_hi_and_version,
+	    ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
+	    ((uint16_t)u->node[0] << 8) | u->node[1],
+	    ((uint16_t)u->node[2] << 8) | u->node[3],
+	    ((uint16_t)u->node[4] << 8) | u->node[5]);
+}
+
+static void uuid_from_str(uuid_t *u, const char *s)
+{
+	int n;
+
+	if (s == NULL)
+		log_errx("UUID cannot be NULL");
+	if (strlen(s) != _UUID_STR_LEN)
+		log_errx("Invalid UUID: %s", s);
+
+	n = sscanf(s,
+	    "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
+	    &u->time_low, &u->time_mid, &u->time_hi_and_version,
+	    &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
+	    &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
+	/*
+	 * Given the format specifier above, we expect 11 items to be scanned
+	 * for a properly formatted UUID.
+	 */
+	if (n != 11)
+		log_errx("Invalid UUID: %s", s);
+}
+
 static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
 {
 	struct stat st;
@@ -300,7 +336,6 @@
 	char *buf, *bufend;
 	fip_toc_header_t *toc_header;
 	fip_toc_entry_t *toc_entry;
-	image_t *image;
 	int terminated = 0;
 
 	fp = fopen(filename, "r");
@@ -331,6 +366,9 @@
 
 	/* Walk through each ToC entry in the file. */
 	while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
+		image_t *image;
+		image_desc_t *desc;
+
 		/* Found the ToC terminator, we are done. */
 		if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
 			terminated = 1;
@@ -356,6 +394,21 @@
 		    toc_entry->size);
 		image->size = toc_entry->size;
 
+		/* If this is an unknown image, create a descriptor for it. */
+		desc = lookup_image_desc_from_uuid(&image->uuid);
+		if (desc == NULL) {
+			char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
+
+			uuid_to_str(name, sizeof(name), &image->uuid);
+			snprintf(filename, sizeof(filename), "%s%s",
+			    name, ".bin");
+			desc = new_image_desc(&image->uuid, name, "blob");
+			desc->action = DO_UNPACK;
+			desc->action_arg = xstrdup(filename,
+			    "failed to allocate memory for blob filename");
+			add_image_desc(desc);
+		}
+
 		add_image(image);
 
 		toc_entry++;
@@ -444,7 +497,7 @@
 {
 	image_t *image;
 	uint64_t image_offset;
-	uint64_t image_size = 0;
+	uint64_t image_size;
 	fip_toc_header_t toc_header;
 
 	if (argc != 2)
@@ -469,10 +522,8 @@
 		image_desc_t *desc;
 
 		desc = lookup_image_desc_from_uuid(&image->uuid);
-		if (desc != NULL)
-			printf("%s: ", desc->name);
-		else
-			printf("Unknown entry: ");
+		assert(desc != NULL);
+		printf("%s: ", desc->name);
 		image_size = image->size;
 		printf("offset=0x%llX, size=0x%llX",
 		    (unsigned long long)image_offset,
@@ -578,10 +629,11 @@
 static void update_fip(void)
 {
 	image_desc_t *desc;
-	image_t *new_image, *old_image;
 
 	/* Add or replace images in the FIP file. */
 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
+		image_t *new_image, *old_image;
+
 		if (desc->action != DO_PACK)
 			continue;
 
@@ -590,7 +642,7 @@
 		old_image = lookup_image_from_uuid(&desc->uuid);
 		if (old_image != NULL) {
 			if (verbose) {
-				log_dbgx("Replacing image %s.bin with %s",
+				log_dbgx("Replacing %s with %s",
 				    desc->cmdline_name,
 				    desc->action_arg);
 			}
@@ -618,6 +670,21 @@
 	*toc_flags |= flags << 32;
 }
 
+static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
+{
+	char *p;
+
+	for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
+		if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
+			p += strlen("uuid=");
+			uuid_from_str(uuid, p);
+		} else if (strncmp(p, "file=", strlen("file=")) == 0) {
+			p += strlen("file=");
+			snprintf(filename, len, "%s", p);
+		}
+	}
+}
+
 static int create_cmd(int argc, char *argv[])
 {
 	struct option *opts = NULL;
@@ -630,12 +697,13 @@
 	opts = fill_common_opts(opts, &nr_opts, required_argument);
 	opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
 	    OPT_PLAT_TOC_FLAGS);
+	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
 
 	while (1) {
 		int c, opt_index = 0;
 
-		c = getopt_long(argc, argv, "", opts, &opt_index);
+		c = getopt_long(argc, argv, "b:", opts, &opt_index);
 		if (c == -1)
 			break;
 
@@ -655,6 +723,36 @@
 		case OPT_PLAT_TOC_FLAGS:
 			parse_plat_toc_flags(optarg, &toc_flags);
 			break;
+		case 'b': {
+			char name[_UUID_STR_LEN + 1];
+			char filename[PATH_MAX] = { 0 };
+			uuid_t uuid = { 0 };
+			image_desc_t *desc;
+
+			parse_blob_opt(optarg, &uuid,
+			    filename, sizeof(filename));
+
+			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
+			    filename[0] == '\0')
+				create_usage();
+
+			desc = lookup_image_desc_from_uuid(&uuid);
+			if (desc != NULL) {
+				if (desc->action != DO_UNSPEC)
+					free(desc->action_arg);
+				desc->action = DO_PACK;
+				desc->action_arg = xstrdup(filename,
+				    "failed to allocate memory for argument");
+			} else {
+				uuid_to_str(name, sizeof(name), &uuid);
+				desc = new_image_desc(&uuid, name, "blob");
+				desc->action = DO_PACK;
+				desc->action_arg = xstrdup(filename,
+				    "failed to allocate memory for argument");
+				add_image_desc(desc);
+			}
+			break;
+		}
 		default:
 			create_usage();
 		}
@@ -677,7 +775,10 @@
 {
 	toc_entry_t *toc_entry = toc_entries;
 
-	printf("fiptool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
+	printf("fiptool create [--blob uuid=...,file=...] "
+	    "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
+	printf("  --blob uuid=...,file=...\tAdd an image with the given UUID "
+	    "pointed to by file.\n");
 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field "
 	    "occupying bits 32-47 in 64-bit ToC header.\n");
 	fputc('\n', stderr);
@@ -692,7 +793,7 @@
 {
 	struct option *opts = NULL;
 	size_t nr_opts = 0;
-	char outfile[FILENAME_MAX] = { 0 };
+	char outfile[PATH_MAX] = { 0 };
 	fip_toc_header_t toc_header = { 0 };
 	unsigned long long toc_flags = 0;
 	int pflag = 0;
@@ -701,6 +802,7 @@
 		update_usage();
 
 	opts = fill_common_opts(opts, &nr_opts, required_argument);
+	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
 	opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
 	opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
 	    OPT_PLAT_TOC_FLAGS);
@@ -709,7 +811,7 @@
 	while (1) {
 		int c, opt_index = 0;
 
-		c = getopt_long(argc, argv, "o:", opts, &opt_index);
+		c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
 		if (c == -1)
 			break;
 
@@ -730,6 +832,36 @@
 			parse_plat_toc_flags(optarg, &toc_flags);
 			pflag = 1;
 			break;
+		case 'b': {
+			char name[_UUID_STR_LEN + 1];
+			char filename[PATH_MAX] = { 0 };
+			uuid_t uuid = { 0 };
+			image_desc_t *desc;
+
+			parse_blob_opt(optarg, &uuid,
+			    filename, sizeof(filename));
+
+			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
+			    filename[0] == '\0')
+				update_usage();
+
+			desc = lookup_image_desc_from_uuid(&uuid);
+			if (desc != NULL) {
+				if (desc->action != DO_UNSPEC)
+					free(desc->action_arg);
+				desc->action = DO_PACK;
+				desc->action_arg = xstrdup(filename,
+				    "failed to allocate memory for argument");
+			} else {
+				uuid_to_str(name, sizeof(name), &uuid);
+				desc = new_image_desc(&uuid, name, "blob");
+				desc->action = DO_PACK;
+				desc->action_arg = xstrdup(filename,
+				    "failed to allocate memory for argument");
+				add_image_desc(desc);
+			}
+			break;
+		}
 		case 'o':
 			snprintf(outfile, sizeof(outfile), "%s", optarg);
 			break;
@@ -765,8 +897,10 @@
 {
 	toc_entry_t *toc_entry = toc_entries;
 
-	printf("fiptool update [--out FIP_FILENAME] "
+	printf("fiptool update [--blob uuid=...,file=...] [--out FIP_FILENAME] "
 	    "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
+	printf("  --blob uuid=...,file=...\tAdd or update an image "
+	    "with the given UUID pointed to by file.\n");
 	printf("  --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field "
 	    "occupying bits 32-47 in 64-bit ToC header.\n");
@@ -782,7 +916,7 @@
 {
 	struct option *opts = NULL;
 	size_t nr_opts = 0;
-	char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 };
+	char outdir[PATH_MAX] = { 0 };
 	image_desc_t *desc;
 	int fflag = 0;
 	int unpack_all = 1;
@@ -791,6 +925,7 @@
 		unpack_usage();
 
 	opts = fill_common_opts(opts, &nr_opts, required_argument);
+	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
 	opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
 	opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
@@ -798,7 +933,7 @@
 	while (1) {
 		int c, opt_index = 0;
 
-		c = getopt_long(argc, argv, "fo:", opts, &opt_index);
+		c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
 		if (c == -1)
 			break;
 
@@ -816,6 +951,37 @@
 			unpack_all = 0;
 			break;
 		}
+		case 'b': {
+			char name[_UUID_STR_LEN + 1];
+			char filename[PATH_MAX] = { 0 };
+			uuid_t uuid = { 0 };
+			image_desc_t *desc;
+
+			parse_blob_opt(optarg, &uuid,
+			    filename, sizeof(filename));
+
+			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
+			    filename[0] == '\0')
+				unpack_usage();
+
+			desc = lookup_image_desc_from_uuid(&uuid);
+			if (desc != NULL) {
+				if (desc->action != DO_UNSPEC)
+					free(desc->action_arg);
+				desc->action = DO_UNPACK;
+				desc->action_arg = xstrdup(filename,
+				    "failed to allocate memory for argument");
+			} else {
+				uuid_to_str(name, sizeof(name), &uuid);
+				desc = new_image_desc(&uuid, name, "blob");
+				desc->action = DO_UNPACK;
+				desc->action_arg = xstrdup(filename,
+				    "failed to allocate memory for argument");
+				add_image_desc(desc);
+			}
+			unpack_all = 0;
+			break;
+		}
 		case 'f':
 			fflag = 1;
 			break;
@@ -841,6 +1007,7 @@
 
 	/* Unpack all specified images. */
 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
+		char file[PATH_MAX];
 		image_t *image;
 
 		if (!unpack_all && desc->action != DO_UNPACK)
@@ -857,7 +1024,7 @@
 		image = lookup_image_from_uuid(&desc->uuid);
 		if (image == NULL) {
 			if (!unpack_all)
-				log_warnx("Requested image %s is not in %s",
+				log_warnx("%s does not exist in %s",
 				    file, argv[0]);
 			continue;
 		}
@@ -880,10 +1047,13 @@
 {
 	toc_entry_t *toc_entry = toc_entries;
 
-	printf("fiptool unpack [--force] [--out <path>] [opts] FIP_FILENAME\n");
-	printf("  --force\tIf the output file already exists, use --force to "
+	printf("fiptool unpack [--blob uuid=...,file=...] [--force] "
+	    "[--out <path>] [opts] FIP_FILENAME\n");
+	printf("  --blob uuid=...,file=...\tUnpack an image with the given UUID "
+	    "to file.\n");
+	printf("  --force\t\t\tIf the output file already exists, use --force to "
 	    "overwrite it.\n");
-	printf("  --out path\tSet the output directory path.\n");
+	printf("  --out path\t\t\tSet the output directory path.\n");
 	fputc('\n', stderr);
 	printf("Specific images are unpacked with the following options:\n");
 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
@@ -898,7 +1068,7 @@
 {
 	struct option *opts = NULL;
 	size_t nr_opts = 0;
-	char outfile[FILENAME_MAX] = { 0 };
+	char outfile[PATH_MAX] = { 0 };
 	fip_toc_header_t toc_header;
 	image_desc_t *desc;
 	int fflag = 0;
@@ -907,6 +1077,7 @@
 		remove_usage();
 
 	opts = fill_common_opts(opts, &nr_opts, no_argument);
+	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
 	opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
 	opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
@@ -914,7 +1085,7 @@
 	while (1) {
 		int c, opt_index = 0;
 
-		c = getopt_long(argc, argv, "fo:", opts, &opt_index);
+		c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
 		if (c == -1)
 			break;
 
@@ -928,6 +1099,30 @@
 			desc->action_arg = NULL;
 			break;
 		}
+		case 'b': {
+			char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
+			uuid_t uuid = { 0 };
+			image_desc_t *desc;
+
+			parse_blob_opt(optarg, &uuid,
+			    filename, sizeof(filename));
+
+			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
+				remove_usage();
+
+			desc = lookup_image_desc_from_uuid(&uuid);
+			if (desc != NULL) {
+				desc->action = DO_REMOVE;
+				desc->action_arg = NULL;
+			} else {
+				uuid_to_str(name, sizeof(name), &uuid);
+				desc = new_image_desc(&uuid, name, "blob");
+				desc->action = DO_REMOVE;
+				desc->action_arg = NULL;
+				add_image_desc(desc);
+			}
+			break;
+		}
 		case 'f':
 			fflag = 1;
 			break;
@@ -959,14 +1154,15 @@
 
 		if (desc->action != DO_REMOVE)
 			continue;
+
 		image = lookup_image_from_uuid(&desc->uuid);
 		if (image != NULL) {
 			if (verbose)
-				log_dbgx("Removing %s.bin",
+				log_dbgx("Removing %s",
 				    desc->cmdline_name);
 			remove_image(image);
 		} else {
-			log_warnx("Requested image %s.bin is not in %s",
+			log_warnx("%s does not exist in %s",
 			    desc->cmdline_name, argv[0]);
 		}
 	}
@@ -980,7 +1176,9 @@
 {
 	toc_entry_t *toc_entry = toc_entries;
 
-	printf("fiptool remove [--force] [--out FIP_FILENAME] [opts] FIP_FILENAME\n");
+	printf("fiptool remove [--blob uuid=...] [--force] "
+	    "[--out FIP_FILENAME] [opts] FIP_FILENAME\n");
+	printf("  --blob uuid=...\tRemove an image with the given UUID.\n");
 	printf("  --force\t\tIf the output FIP file already exists, use --force to "
 	    "overwrite it.\n");
 	printf("  --out FIP_FILENAME\tSet an alternative output FIP file.\n");