diff mbox series

[RFC,1/1] tools: mkimage: Add Allwinner TOC1 support

Message ID 20211015040811.56856-2-samuel@sholland.org
State RFC
Delegated to: Andre Przywara
Headers show
Series [RFC,1/1] tools: mkimage: Add Allwinner TOC1 support | expand

Commit Message

Samuel Holland Oct. 15, 2021, 4:08 a.m. UTC
TOC1 is an container format used by Allwinner's boot0 that can hold
multiple images. It supports encryption and signatures, but that
functionality is not implemented, only the basic "non-secure" subset.

A config file is used to provide the list of data files to include. Its
path is passed as the argument to "-d". It contains sections of the
following form:

  [name]
  file = /path/to/file
  addr = 0x12345678

Specific well-known names, such as "dtb", "opensbi", and "u-boot", are
used by the bootloader to distinguish the items inside the image.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

 common/image.c        |   1 +
 include/image.h       |   1 +
 include/sunxi_image.h |  26 ++++
 tools/Makefile        |   1 +
 tools/sunxi_toc1.c    | 318 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 347 insertions(+)
 create mode 100644 tools/sunxi_toc1.c
diff mbox series

Patch

diff --git a/common/image.c b/common/image.c
index d15b47ebbe..a2b925c0c5 100644
--- a/common/image.c
+++ b/common/image.c
@@ -186,6 +186,7 @@  static const table_entry_t uimage_type[] = {
 	{	IH_TYPE_COPRO, "copro", "Coprocessor Image"},
 	{	IH_TYPE_SUNXI_EGON, "sunxi_egon",  "Allwinner eGON Boot Image" },
 	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
+	{	IH_TYPE_SUNXI_TOC1, "sunxi_toc1",  "Allwinner TOC1 Boot Image" },
 	{	-1,		    "",		  "",			},
 };
 
diff --git a/include/image.h b/include/image.h
index 1547246ec8..eb165f34da 100644
--- a/include/image.h
+++ b/include/image.h
@@ -228,6 +228,7 @@  enum {
 	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
 	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
 	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
+	IH_TYPE_SUNXI_TOC1,		/* Allwinner TOC1 Boot Image */
 
 	IH_TYPE_COUNT,			/* Number of image types */
 };
diff --git a/include/sunxi_image.h b/include/sunxi_image.h
index 379ca9196e..98f381bbe6 100644
--- a/include/sunxi_image.h
+++ b/include/sunxi_image.h
@@ -116,4 +116,30 @@  struct __packed toc0_item_info {
 #define TOC0_ITEM_INFO_NAME_KEY		0x00010303
 #define TOC0_ITEM_INFO_END		"IIE;"
 
+struct __packed toc1_main_info {
+	uint8_t name[16];
+	__le32  magic;
+	__le32  checksum;
+	__le32  serial;
+	__le32  status;
+	__le32  num_items;
+	__le32  length;
+	__le32	major_version;
+	__le32	minor_version;
+	__le32	reserved[3];
+	uint8_t end[4];
+};
+
+struct __packed toc1_item_info {
+	uint8_t name[64];
+	__le32  offset;
+	__le32  length;
+	__le32  encryption;
+	__le32  type;
+	__le32  load_addr;
+	__le32  index;
+	__le32	reserved[69];
+	uint8_t end[4];
+};
+
 #endif
diff --git a/tools/Makefile b/tools/Makefile
index e2aeb097aa..8b42f3ff6f 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -132,6 +132,7 @@  dumpimage-mkimage-objs := aisimage.o \
 			$(ROCKCHIP_OBS) \
 			socfpgaimage.o \
 			sunxi_egon.o \
+			sunxi_toc1.o \
 			lib/crc16.o \
 			lib/hash-checksum.o \
 			lib/sha1.o \
diff --git a/tools/sunxi_toc1.c b/tools/sunxi_toc1.c
new file mode 100644
index 0000000000..7dd3f5d52a
--- /dev/null
+++ b/tools/sunxi_toc1.c
@@ -0,0 +1,318 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Arm Ltd.
+ * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <image.h>
+#include <sunxi_image.h>
+
+#include "imagetool.h"
+#include "mkimage.h"
+
+#define SECTOR_SIZE			512
+
+struct item_desc {
+	const char	*name;
+	const char	*file;
+	unsigned long	addr;
+	long		length;
+};
+
+static uint32_t toc1_header_length(uint32_t num_items)
+{
+	return ALIGN(sizeof(struct toc1_main_info) +
+		     sizeof(struct toc1_item_info) * num_items, SECTOR_SIZE);
+}
+
+static int toc1_parse_cfg(const char *file, struct item_desc **desc,
+			  uint32_t *main_length, uint32_t *num_items)
+{
+	struct item_desc *descs = NULL;
+	int ret = EXIT_FAILURE;
+	FILE *cfg, *fp = NULL;
+	uint32_t ndescs = 0;
+	char *line = NULL;
+	size_t len = 0;
+
+	*desc = NULL;
+	*main_length = 0;
+	*num_items = 0;
+
+	cfg = fopen(file, "r");
+	if (!cfg)
+		return ret;
+
+	while (getline(&line, &len, cfg) > 0) {
+		char *end, *s;
+
+		if (line[0] == '[') {
+			s = line + 1;
+			end = strchr(s, ']');
+			if (!end || end[1] != '\n')
+				goto err;
+			end[0] = '\0';
+
+			ndescs++;
+			descs = reallocarray(descs, ndescs, sizeof(*descs));
+			if (!descs)
+				goto err;
+
+			descs[ndescs - 1].name = strdup(s);
+		} else if (line[0] != '#' && line[0] != '\n') {
+			s = strchr(line, '=');
+			if (!s)
+				goto err;
+			while ((++s)[0] == ' ')
+				;
+			end = strchr(s, '\n');
+			if (!end)
+				goto err;
+			end[0] = '\0';
+
+			if (!strncmp(line, "file", strlen("file"))) {
+				fp = fopen(s, "rb");
+				if (!fp)
+					goto err;
+				if (fseek(fp, 0, SEEK_END) < 0)
+					goto err;
+				descs[ndescs - 1].file = strdup(s);
+				descs[ndescs - 1].length = ftell(fp);
+				*main_length += ALIGN(descs[ndescs - 1].length,
+						      SECTOR_SIZE);
+				fclose(fp);
+				fp = NULL;
+			} else if (!strncmp(line, "addr", strlen("addr"))) {
+				descs[ndescs - 1].addr = strtoul(s, NULL, 0);
+			} else {
+				goto err;
+			}
+		}
+	}
+
+	*desc = descs;
+	*main_length += toc1_header_length(ndescs);
+	*num_items = ndescs;
+
+	ret = EXIT_SUCCESS;
+
+err:
+	if (fp)
+		fclose(fp);
+	if (ret)
+		free(descs);
+	free(line);
+	fclose(cfg);
+
+	return ret;
+}
+
+static int toc1_create(uint8_t *buf, uint32_t len,
+		       const struct item_desc *desc, uint32_t num_items)
+{
+	struct toc1_main_info *main = (void *)buf;
+	struct toc1_item_info *item = (void *)(main + 1);
+	uint32_t item_offset, item_length;
+	uint32_t *buf32 = (void *)buf;
+	int ret = EXIT_FAILURE;
+	uint32_t checksum = 0;
+	FILE *fp = NULL;
+	int i;
+
+	/* Create the main TOC1 header. */
+	main->magic	= cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
+	main->checksum	= cpu_to_le32(BROM_STAMP_VALUE);
+	main->num_items	= cpu_to_le32(num_items);
+	memcpy(main->end, TOC0_MAIN_INFO_END, sizeof(main->end));
+
+	item_offset = 0;
+	item_length = toc1_header_length(num_items);
+
+	for (i = 0; i < num_items; ++i, ++item, ++desc) {
+		item_offset = item_offset + item_length;
+		item_length = desc->length;
+
+		/* Create the item header. */
+		memcpy(item->name, desc->name,
+		       strnlen(desc->name, sizeof(item->name)));
+		item->offset = cpu_to_le32(item_offset);
+		item->length = cpu_to_le32(item_length);
+		item->load_addr = cpu_to_le32(desc->addr);
+		memcpy(item->end, TOC0_ITEM_INFO_END, sizeof(item->end));
+
+		/* Read in the data. */
+		fp = fopen(desc->file, "rb");
+		if (!fp)
+			goto err;
+		if (!fread(buf + item_offset, item_length, 1, fp))
+			goto err;
+		fclose(fp);
+		fp = NULL;
+
+		/* Pad the sectors with 0xff to be flash-friendly. */
+		item_offset = item_offset + item_length;
+		item_length = ALIGN(item_offset, SECTOR_SIZE) - item_offset;
+		memset(buf + item_offset, 0xff, item_length);
+	}
+
+	/* Fill in the total padded file length. */
+	item_offset = item_offset + item_length;
+	main->length = cpu_to_le32(item_offset);
+
+	/* Verify enough space was provided when creating the image. */
+	assert(len >= item_offset);
+
+	/* Calculate the checksum. Yes, it's that simple. */
+	for (i = 0; i < item_offset / 4; ++i)
+		checksum += le32_to_cpu(buf32[i]);
+	main->checksum = cpu_to_le32(checksum);
+
+	ret = EXIT_SUCCESS;
+
+err:
+	if (fp)
+		fclose(fp);
+
+	return ret;
+}
+
+static int toc1_verify(const uint8_t *buf, uint32_t len)
+{
+	const struct toc1_main_info *main = (void *)buf;
+	const struct toc1_item_info *item = (void *)(main + 1);
+	uint32_t checksum = BROM_STAMP_VALUE;
+	uint32_t main_length, num_items;
+	uint32_t *buf32 = (void *)buf;
+	int ret = EXIT_FAILURE;
+	int i;
+
+	num_items   = le32_to_cpu(main->num_items);
+	main_length = le32_to_cpu(main->length);
+
+	if (len < main_length || main_length < toc1_header_length(num_items))
+		goto err;
+
+	/* Verify the main header. */
+	if (le32_to_cpu(main->magic) != TOC0_MAIN_INFO_MAGIC)
+		goto err;
+	/* Verify the checksum without modifying the buffer. */
+	for (i = 0; i < main_length / 4; ++i)
+		checksum += le32_to_cpu(buf32[i]);
+	if (checksum != 2 * le32_to_cpu(main->checksum))
+		goto err;
+	/* The length must be at least 512 byte aligned. */
+	if (main_length % SECTOR_SIZE)
+		goto err;
+	if (memcmp(main->end, TOC0_MAIN_INFO_END, sizeof(main->end)))
+		goto err;
+
+	/* Verify each item header. */
+	for (i = 0; i < num_items; ++i, ++item)
+		if (memcmp(item->end, TOC0_ITEM_INFO_END, sizeof(item->end)))
+			goto err;
+
+	ret = EXIT_SUCCESS;
+
+err:
+	return ret;
+}
+
+static int toc1_check_params(struct image_tool_params *params)
+{
+	if (!params->dflag)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int toc1_verify_header(unsigned char *buf, int image_size,
+			      struct image_tool_params *params)
+{
+	return toc1_verify(buf, image_size);
+}
+
+static void toc1_print_header(const void *buf)
+{
+	const struct toc1_main_info *main = buf;
+	const struct toc1_item_info *item = (void *)(main + 1);
+	uint32_t head_length, main_length, num_items;
+	uint32_t item_offset, item_length, item_addr;
+	int i;
+
+	num_items   = le32_to_cpu(main->num_items);
+	head_length = sizeof(*main) + num_items * sizeof(*item);
+	main_length = le32_to_cpu(main->length);
+
+	printf("Allwinner TOC1 Image\n"
+	       "Size: %d bytes\n"
+	       "Contents: %d items\n"
+	       " 00000000:%08x Headers\n",
+	       main_length, num_items, head_length);
+
+	for (i = 0; i < num_items; ++i, ++item) {
+		item_offset = le32_to_cpu(item->offset);
+		item_length = le32_to_cpu(item->length);
+		item_addr   = le32_to_cpu(item->load_addr);
+
+		printf(" %08x:%08x => %08x %s\n",
+		       item_offset, item_length, item_addr, item->name);
+	}
+}
+
+static void toc1_set_header(void *buf, struct stat *sbuf, int ifd,
+			    struct image_tool_params *params)
+{
+	/* Image is already written below. */
+}
+
+static int toc1_check_image_type(uint8_t type)
+{
+	return type == IH_TYPE_SUNXI_TOC1 ? 0 : 1;
+}
+
+static int toc1_vrec_header(struct image_tool_params *params,
+			    struct image_type_params *tparams)
+{
+	uint32_t main_length, num_items;
+	struct item_desc *desc;
+	int ret;
+
+	/* This "header" contains the entire image. */
+	params->skipcpy = 1;
+
+	ret = toc1_parse_cfg(params->datafile, &desc, &main_length, &num_items);
+	if (ret)
+		exit(ret);
+
+	tparams->header_size = main_length;
+	tparams->hdr = calloc(tparams->header_size, 1);
+	if (!tparams->hdr)
+		exit(ret);
+
+	ret = toc1_create(tparams->hdr, tparams->header_size, desc, num_items);
+	if (ret)
+		exit(ret);
+
+	return 0;
+}
+
+U_BOOT_IMAGE_TYPE(
+	sunxi_toc1,
+	"Allwinner TOC1 Boot Image support",
+	0,
+	NULL,
+	toc1_check_params,
+	toc1_verify_header,
+	toc1_print_header,
+	toc1_set_header,
+	NULL,
+	toc1_check_image_type,
+	NULL,
+	toc1_vrec_header
+);