fiptool: Prepare ground for expanding the set of images at runtime

To allow operating on images with unknown UUIDs, fiptool needs to
be able to track an arbitrary amount of images and not be limited
to the set of images described by the builtin table.

Convert the table to a list to accommodate this scenario.

Change-Id: I0e6d738eece7795d74fc72d165a3098f223d4414
Signed-off-by: dp-arm <dimitris.papastamos@arm.com>
diff --git a/tools/fiptool/fiptool.c b/tools/fiptool/fiptool.c
index 7145815..70921ce 100644
--- a/tools/fiptool/fiptool.c
+++ b/tools/fiptool/fiptool.c
@@ -51,6 +51,8 @@
 #define OPT_TOC_ENTRY 0
 #define OPT_PLAT_TOC_FLAGS 1
 
+static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid);
+static image_t *lookup_image_from_uuid(const uuid_t *uuid);
 static int info_cmd(int argc, char *argv[]);
 static void info_usage(void);
 static int create_cmd(int argc, char *argv[]);
@@ -77,7 +79,9 @@
 	{ .name = "help",    .handler = help_cmd,    .usage = NULL          },
 };
 
-static image_t *images[MAX_IMAGES];
+static image_desc_t *image_desc_head;
+static size_t nr_image_descs;
+static image_t *image_head;
 static size_t nr_images;
 static uuid_t uuid_null = { 0 };
 static int verbose;
@@ -151,82 +155,141 @@
 	return d;
 }
 
-static void add_image(image_t *image)
+static image_desc_t *new_image_desc(const uuid_t *uuid,
+    const char *name, const char *cmdline_name)
 {
-	if (nr_images + 1 > MAX_IMAGES)
-		log_errx("Too many images");
-	images[nr_images++] = image;
+	image_desc_t *desc;
+
+	desc = xmalloc(sizeof(*desc),
+	    "failed to allocate memory for image descriptor");
+	memcpy(&desc->uuid, uuid, sizeof(uuid_t));
+	desc->name = xstrdup(name,
+	    "failed to allocate memory for image name");
+	desc->cmdline_name = xstrdup(cmdline_name,
+	    "failed to allocate memory for image command line name");
+	desc->action = DO_UNSPEC;
+	desc->action_arg = NULL;
+	return desc;
 }
 
-static void free_image(image_t *image)
+static void free_image_desc(image_desc_t *desc)
 {
-	free(image->buffer);
-	free(image);
+	free(desc->name);
+	free(desc->cmdline_name);
+	free(desc->action_arg);
+	free(desc);
 }
 
-static void replace_image(image_t *image_dst, image_t *image_src)
+static void add_image_desc(image_desc_t *desc)
 {
-	int i;
+	assert(lookup_image_desc_from_uuid(&desc->uuid) == NULL);
+	desc->next = image_desc_head;
+	image_desc_head = desc;
+	nr_image_descs++;
+}
 
-	for (i = 0; i < nr_images; i++) {
-		if (images[i] == image_dst) {
-			free_image(images[i]);
-			images[i] = image_src;
-			break;
-		}
+static void free_image_descs(void)
+{
+	image_desc_t *desc = image_desc_head, *tmp;
+
+	while (desc != NULL) {
+		tmp = desc->next;
+		free_image_desc(desc);
+		desc = tmp;
+		nr_image_descs--;
 	}
-	assert(i != nr_images);
+	assert(nr_image_descs == 0);
 }
 
+static void fill_image_descs(void)
+{
+	toc_entry_t *toc_entry;
+
+	for (toc_entry = toc_entries;
+	     toc_entry->cmdline_name != NULL;
+	     toc_entry++) {
+		image_desc_t *desc;
+
+		desc = new_image_desc(&toc_entry->uuid,
+		    toc_entry->name,
+		    toc_entry->cmdline_name);
+		add_image_desc(desc);
+	}
+}
+
+static void add_image(image_t *image)
+{
+	assert(lookup_image_from_uuid(&image->uuid) == NULL);
+	image->next = image_head;
+	image_head = image;
+	nr_images++;
+}
+
+static void free_image(image_t *image)
+{
+	free(image->buffer);
+	free(image);
+}
+
 static void remove_image(image_t *image)
 {
-	int i;
+	image_t *tmp = image_head, *prev;
 
-	for (i = 0; i < nr_images; i++) {
-		if (images[i] == image) {
-			free_image(images[i]);
-			images[i] = NULL;
-			break;
+	if (tmp == image) {
+		image_head = tmp->next;
+		free_image(tmp);
+	} else {
+		while (tmp != NULL && tmp != image) {
+			prev = tmp;
+			tmp = tmp->next;
 		}
+		assert(tmp != NULL);
+		prev->next = tmp->next;
+		free_image(tmp);
 	}
-	assert(i != nr_images);
-
-	/* Compact array. */
-	memmove(&images[i], &images[i + 1],
-	    (nr_images - i - 1) * sizeof(*images));
 	nr_images--;
 }
 
 static void free_images(void)
 {
-	int i;
+	image_t *image = image_head, *tmp;
 
-	for (i = 0; i < nr_images; i++) {
-		free_image(images[i]);
-		images[i] = NULL;
+	while (image != NULL) {
+		tmp = image->next;
+		free_image(image);
+		image = tmp;
+		nr_images--;
 	}
+	assert(nr_images == 0);
 }
 
-static toc_entry_t *lookup_entry_from_uuid(uuid_t *uuid)
+static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
 {
-	toc_entry_t *toc_entry = toc_entries;
+	image_desc_t *desc;
 
-	for (; toc_entry->cmdline_name != NULL; toc_entry++)
-		if (memcmp(&toc_entry->uuid, uuid, sizeof(uuid_t)) == 0)
-			return toc_entry;
+	for (desc = image_desc_head; desc != NULL; desc = desc->next)
+		if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
+			return desc;
 	return NULL;
 }
 
-static image_t *lookup_image_from_uuid(uuid_t *uuid)
+static image_desc_t *lookup_image_desc_from_opt(const char *opt)
+{
+	image_desc_t *desc;
+
+	for (desc = image_desc_head; desc != NULL; desc = desc->next)
+		if (strcmp(desc->cmdline_name, opt) == 0)
+			return desc;
+	return NULL;
+}
+
+static image_t *lookup_image_from_uuid(const uuid_t *uuid)
 {
 	image_t *image;
-	int i;
 
-	for (i = 0; i < nr_images; i++) {
-		image = images[i];
+	for (image = image_head; image != NULL; image = image->next)
 		if (memcmp(&image->uuid, uuid, sizeof(uuid_t)) == 0)
 			return image;
-	}
 	return NULL;
 }
 
@@ -344,29 +407,32 @@
 	return 0;
 }
 
-static int fill_common_opts(struct option *opts, int has_arg)
+static struct option *add_opt(struct option *opts, size_t *nr_opts,
+    const char *name, int has_arg, int val)
 {
-	int i;
-
-	for (i = 0; toc_entries[i].cmdline_name != NULL; i++) {
-		opts[i].name = toc_entries[i].cmdline_name;
-		opts[i].has_arg = has_arg;
-		opts[i].flag = NULL;
-		opts[i].val = 0;
-	}
-	return i;
+	opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
+	if (opts == NULL)
+		log_err("realloc");
+	opts[*nr_opts].name = name;
+	opts[*nr_opts].has_arg = has_arg;
+	opts[*nr_opts].flag = NULL;
+	opts[*nr_opts].val = val;
+	++*nr_opts;
+	return opts;
 }
 
-static void add_opt(struct option *opts, int idx, char *name,
-    int has_arg, int val)
+static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
+    int has_arg)
 {
-	opts[idx].name = name;
-	opts[idx].has_arg = has_arg;
-	opts[idx].flag = NULL;
-	opts[idx].val = val;
+	image_desc_t *desc;
+
+	for (desc = image_desc_head; desc != NULL; desc = desc->next)
+		opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
+		    OPT_TOC_ENTRY);
+	return opts;
 }
 
-static void md_print(unsigned char *md, size_t len)
+static void md_print(const unsigned char *md, size_t len)
 {
 	size_t i;
 
@@ -380,7 +446,6 @@
 	uint64_t image_offset;
 	uint64_t image_size = 0;
 	fip_toc_header_t toc_header;
-	int i;
 
 	if (argc != 2)
 		info_usage();
@@ -400,22 +465,21 @@
 	image_offset = sizeof(fip_toc_header_t) +
 	    (sizeof(fip_toc_entry_t) * (nr_images + 1));
 
-	for (i = 0; i < nr_images; i++) {
-		toc_entry_t *toc_entry;
+	for (image = image_head; image != NULL; image = image->next) {
+		image_desc_t *desc;
 
-		image = images[i];
-		toc_entry = lookup_entry_from_uuid(&image->uuid);
-		if (toc_entry != NULL)
-			printf("%s: ", toc_entry->name);
+		desc = lookup_image_desc_from_uuid(&image->uuid);
+		if (desc != NULL)
+			printf("%s: ", desc->name);
 		else
 			printf("Unknown entry: ");
 		image_size = image->size;
 		printf("offset=0x%llX, size=0x%llX",
 		    (unsigned long long)image_offset,
 		    (unsigned long long)image_size);
-		if (toc_entry != NULL)
+		if (desc != NULL)
 			printf(", cmdline=\"--%s\"",
-			    toc_entry->cmdline_name);
+			    desc->cmdline_name);
 		if (verbose) {
 			unsigned char md[SHA256_DIGEST_LENGTH];
 
@@ -437,7 +501,7 @@
 	exit(1);
 }
 
-static int pack_images(char *filename, uint64_t toc_flags)
+static int pack_images(const char *filename, uint64_t toc_flags)
 {
 	FILE *fp;
 	image_t *image;
@@ -445,12 +509,11 @@
 	fip_toc_entry_t *toc_entry;
 	char *buf;
 	uint64_t entry_offset, buf_size, payload_size;
-	int i;
 
 	/* Calculate total payload size and allocate scratch buffer. */
 	payload_size = 0;
-	for (i = 0; i < nr_images; i++)
-		payload_size += images[i]->size;
+	for (image = image_head; image != NULL; image = image->next)
+		payload_size += image->size;
 
 	buf_size = sizeof(fip_toc_header_t) +
 	    sizeof(fip_toc_entry_t) * (nr_images + 1);
@@ -467,8 +530,7 @@
 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
 
 	entry_offset = buf_size;
-	for (i = 0; i < nr_images; i++) {
-		image = images[i];
+	for (image = image_head; image != NULL; image = image->next) {
 		memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t));
 		toc_entry->offset_address = entry_offset;
 		toc_entry->size = image->size;
@@ -498,11 +560,9 @@
 	if (verbose)
 		log_dbgx("Payload size: %zu bytes", payload_size);
 
-	for (i = 0; i < nr_images; i++) {
-		image = images[i];
+	for (image = image_head; image != NULL; image = image->next)
 		if (fwrite(image->buffer, 1, image->size, fp) != image->size)
 			log_errx("Failed to write image to %s", filename);
-	}
 
 	fclose(fp);
 	return 0;
@@ -517,38 +577,35 @@
  */
 static void update_fip(void)
 {
-	toc_entry_t *toc_entry;
+	image_desc_t *desc;
 	image_t *new_image, *old_image;
 
 	/* Add or replace images in the FIP file. */
-	for (toc_entry = toc_entries;
-	     toc_entry->cmdline_name != NULL;
-	     toc_entry++) {
-		if (toc_entry->action != DO_PACK)
+	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
+		if (desc->action != DO_PACK)
 			continue;
 
-		new_image = read_image_from_file(&toc_entry->uuid,
-		    toc_entry->action_arg);
-		old_image = lookup_image_from_uuid(&toc_entry->uuid);
+		new_image = read_image_from_file(&desc->uuid,
+		    desc->action_arg);
+		old_image = lookup_image_from_uuid(&desc->uuid);
 		if (old_image != NULL) {
-			if (verbose)
+			if (verbose) {
 				log_dbgx("Replacing image %s.bin with %s",
-				    toc_entry->cmdline_name,
-				    toc_entry->action_arg);
-			replace_image(old_image, new_image);
+				    desc->cmdline_name,
+				    desc->action_arg);
+			}
+			remove_image(old_image);
+			add_image(new_image);
 		} else {
 			if (verbose)
 				log_dbgx("Adding image %s",
-				    toc_entry->action_arg);
+				    desc->action_arg);
 			add_image(new_image);
 		}
-
-		free(toc_entry->action_arg);
-		toc_entry->action_arg = NULL;
 	}
 }
 
-static void parse_plat_toc_flags(char *arg, unsigned long long *toc_flags)
+static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
 {
 	unsigned long long flags;
 	char *endptr;
@@ -563,17 +620,17 @@
 
 static int create_cmd(int argc, char *argv[])
 {
-	struct option opts[toc_entries_len + 1];
+	struct option *opts = NULL;
+	size_t nr_opts = 0;
 	unsigned long long toc_flags = 0;
-	int i;
 
 	if (argc < 2)
 		create_usage();
 
-	i = fill_common_opts(opts, required_argument);
-	add_opt(opts, i, "plat-toc-flags", required_argument,
+	opts = fill_common_opts(opts, &nr_opts, required_argument);
+	opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
 	    OPT_PLAT_TOC_FLAGS);
-	add_opt(opts, ++i, NULL, 0, 0);
+	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
 
 	while (1) {
 		int c, opt_index = 0;
@@ -584,11 +641,14 @@
 
 		switch (c) {
 		case OPT_TOC_ENTRY: {
-			toc_entry_t *toc_entry;
+			image_desc_t *desc;
 
-			toc_entry = &toc_entries[opt_index];
-			toc_entry->action = DO_PACK;
-			toc_entry->action_arg = xstrdup(optarg,
+			desc = lookup_image_desc_from_opt(opts[opt_index].name);
+			assert(desc != NULL);
+			if (desc->action != DO_UNSPEC)
+				free(desc->action_arg);
+			desc->action = DO_PACK;
+			desc->action_arg = xstrdup(optarg,
 			    "failed to allocate memory for argument");
 			break;
 		}
@@ -601,6 +661,7 @@
 	}
 	argc -= optind;
 	argv += optind;
+	free(opts);
 
 	if (argc == 0)
 		create_usage();
@@ -629,21 +690,21 @@
 
 static int update_cmd(int argc, char *argv[])
 {
-	struct option opts[toc_entries_len + 2];
+	struct option *opts = NULL;
+	size_t nr_opts = 0;
 	char outfile[FILENAME_MAX] = { 0 };
 	fip_toc_header_t toc_header = { 0 };
 	unsigned long long toc_flags = 0;
 	int pflag = 0;
-	int i;
 
 	if (argc < 2)
 		update_usage();
 
-	i = fill_common_opts(opts, required_argument);
-	add_opt(opts, i, "out", required_argument, 'o');
-	add_opt(opts, ++i, "plat-toc-flags", required_argument,
+	opts = fill_common_opts(opts, &nr_opts, required_argument);
+	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);
-	add_opt(opts, ++i, NULL, 0, 0);
+	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
 
 	while (1) {
 		int c, opt_index = 0;
@@ -654,19 +715,21 @@
 
 		switch (c) {
 		case OPT_TOC_ENTRY: {
-			toc_entry_t *toc_entry;
+			image_desc_t *desc;
 
-			toc_entry = &toc_entries[opt_index];
-			toc_entry->action = DO_PACK;
-			toc_entry->action_arg = xstrdup(optarg,
+			desc = lookup_image_desc_from_opt(opts[opt_index].name);
+			assert(desc != NULL);
+			if (desc->action != DO_UNSPEC)
+				free(desc->action_arg);
+			desc->action = DO_PACK;
+			desc->action_arg = xstrdup(optarg,
 			    "failed to allocate memory for argument");
 			break;
 		}
-		case OPT_PLAT_TOC_FLAGS: {
+		case OPT_PLAT_TOC_FLAGS:
 			parse_plat_toc_flags(optarg, &toc_flags);
 			pflag = 1;
 			break;
-		}
 		case 'o':
 			snprintf(outfile, sizeof(outfile), "%s", optarg);
 			break;
@@ -676,6 +739,7 @@
 	}
 	argc -= optind;
 	argv += optind;
+	free(opts);
 
 	if (argc == 0)
 		update_usage();
@@ -716,20 +780,20 @@
 
 static int unpack_cmd(int argc, char *argv[])
 {
-	struct option opts[toc_entries_len + 3];
+	struct option *opts = NULL;
+	size_t nr_opts = 0;
 	char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 };
-	toc_entry_t *toc_entry;
+	image_desc_t *desc;
 	int fflag = 0;
 	int unpack_all = 1;
-	int i;
 
 	if (argc < 2)
 		unpack_usage();
 
-	i = fill_common_opts(opts, required_argument);
-	add_opt(opts, i, "force", no_argument, 'f');
-	add_opt(opts, ++i, "out", required_argument, 'o');
-	add_opt(opts, ++i, NULL, 0, 0);
+	opts = fill_common_opts(opts, &nr_opts, required_argument);
+	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);
 
 	while (1) {
 		int c, opt_index = 0;
@@ -739,13 +803,19 @@
 			break;
 
 		switch (c) {
-		case OPT_TOC_ENTRY:
-			unpack_all = 0;
-			toc_entry = &toc_entries[opt_index];
-			toc_entry->action = DO_UNPACK;
-			toc_entry->action_arg = xstrdup(optarg,
+		case OPT_TOC_ENTRY: {
+			image_desc_t *desc;
+
+			desc = lookup_image_desc_from_opt(opts[opt_index].name);
+			assert(desc != NULL);
+			if (desc->action != DO_UNSPEC)
+				free(desc->action_arg);
+			desc->action = DO_UNPACK;
+			desc->action_arg = xstrdup(optarg,
 			    "failed to allocate memory for argument");
+			unpack_all = 0;
 			break;
+		}
 		case 'f':
 			fflag = 1;
 			break;
@@ -758,6 +828,7 @@
 	}
 	argc -= optind;
 	argv += optind;
+	free(opts);
 
 	if (argc == 0)
 		unpack_usage();
@@ -769,29 +840,25 @@
 			log_err("chdir %s", outdir);
 
 	/* Unpack all specified images. */
-	for (toc_entry = toc_entries;
-	     toc_entry->cmdline_name != NULL;
-	     toc_entry++) {
+	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
 		image_t *image;
 
-		if (!unpack_all && toc_entry->action != DO_UNPACK)
+		if (!unpack_all && desc->action != DO_UNPACK)
 			continue;
 
 		/* Build filename. */
-		if (toc_entry->action_arg == NULL)
+		if (desc->action_arg == NULL)
 			snprintf(file, sizeof(file), "%s.bin",
-			    toc_entry->cmdline_name);
+			    desc->cmdline_name);
 		else
 			snprintf(file, sizeof(file), "%s",
-			    toc_entry->action_arg);
+			    desc->action_arg);
 
-		image = lookup_image_from_uuid(&toc_entry->uuid);
+		image = lookup_image_from_uuid(&desc->uuid);
 		if (image == NULL) {
 			if (!unpack_all)
 				log_warnx("Requested image %s is not in %s",
 				    file, argv[0]);
-			free(toc_entry->action_arg);
-			toc_entry->action_arg = NULL;
 			continue;
 		}
 
@@ -803,9 +870,6 @@
 			log_warnx("File %s already exists, use --force to overwrite it",
 			    file);
 		}
-
-		free(toc_entry->action_arg);
-		toc_entry->action_arg = NULL;
 	}
 
 	free_images();
@@ -832,20 +896,20 @@
 
 static int remove_cmd(int argc, char *argv[])
 {
-	struct option opts[toc_entries_len + 2];
+	struct option *opts = NULL;
+	size_t nr_opts = 0;
 	char outfile[FILENAME_MAX] = { 0 };
 	fip_toc_header_t toc_header;
-	toc_entry_t *toc_entry;
+	image_desc_t *desc;
 	int fflag = 0;
-	int i;
 
 	if (argc < 2)
 		remove_usage();
 
-	i = fill_common_opts(opts, no_argument);
-	add_opt(opts, i, "force", no_argument, 'f');
-	add_opt(opts, ++i, "out", required_argument, 'o');
-	add_opt(opts, ++i, NULL, 0, 0);
+	opts = fill_common_opts(opts, &nr_opts, no_argument);
+	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);
 
 	while (1) {
 		int c, opt_index = 0;
@@ -855,10 +919,15 @@
 			break;
 
 		switch (c) {
-		case OPT_TOC_ENTRY:
-			toc_entry = &toc_entries[opt_index];
-			toc_entry->action = DO_REMOVE;
+		case OPT_TOC_ENTRY: {
+			image_desc_t *desc;
+
+			desc = lookup_image_desc_from_opt(opts[opt_index].name);
+			assert(desc != NULL);
+			desc->action = DO_REMOVE;
+			desc->action_arg = NULL;
 			break;
+		}
 		case 'f':
 			fflag = 1;
 			break;
@@ -871,6 +940,7 @@
 	}
 	argc -= optind;
 	argv += optind;
+	free(opts);
 
 	if (argc == 0)
 		remove_usage();
@@ -884,22 +954,20 @@
 
 	parse_fip(argv[0], &toc_header);
 
-	for (toc_entry = toc_entries;
-	     toc_entry->cmdline_name != NULL;
-	     toc_entry++) {
+	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
 		image_t *image;
 
-		if (toc_entry->action != DO_REMOVE)
+		if (desc->action != DO_REMOVE)
 			continue;
-		image = lookup_image_from_uuid(&toc_entry->uuid);
+		image = lookup_image_from_uuid(&desc->uuid);
 		if (image != NULL) {
 			if (verbose)
 				log_dbgx("Removing %s.bin",
-				    toc_entry->cmdline_name);
+				    desc->cmdline_name);
 			remove_image(image);
 		} else {
 			log_warnx("Requested image %s.bin is not in %s",
-			    toc_entry->cmdline_name, argv[0]);
+			    desc->cmdline_name, argv[0]);
 		}
 	}
 
@@ -1011,6 +1079,7 @@
 	if (argc == 0)
 		usage();
 
+	fill_image_descs();
 	for (i = 0; i < NELEM(cmds); i++) {
 		if (strcmp(cmds[i].name, argv[0]) == 0) {
 			ret = cmds[i].handler(argc, argv);
@@ -1019,5 +1088,6 @@
 	}
 	if (i == NELEM(cmds))
 		usage();
+	free_image_descs();
 	return ret;
 }