442 lines
12 KiB
Diff
442 lines
12 KiB
Diff
From ff0e952a3a380ba191375d5f68609cdbe026d535 Mon Sep 17 00:00:00 2001
|
|
From: Samuel Holland <samuel@sholland.org>
|
|
Date: Sat, 7 Aug 2021 19:55:20 -0500
|
|
Subject: [PATCH 33/90] tools: mkimage: Add Allwinner TOC1 support
|
|
|
|
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.
|
|
|
|
Cover-letter:
|
|
tools: mkimage: Add Allwinner TOC1 support
|
|
|
|
The SPL port for the Allwinner D1 RISC-V SoC will probably take a while
|
|
longer than porting U-Boot proper, as none of the relevant drivers are
|
|
set up for DM in SPL. In the meantime, we are using[1][2] a fork[3] of
|
|
Allwinner's boot0 loader, which they also call "spl" in their BSP. boot0
|
|
uses this TOC1 image format.
|
|
|
|
The vendor tools for generating TOC1 images require a binary config file
|
|
generated by their FEX compiler. Instead of trying to support that, I
|
|
made up a simple human-readable config file format. I didn't see any
|
|
existing platform-agnostic parser for multi-image containers in mkimage.
|
|
|
|
I am sending this as RFC because it is only of temporary/limited use.
|
|
It only works with one specific fork of boot0 which was modified to
|
|
"behave" (the the original vendor version monkey-patches a custom header
|
|
inside the U-Boot image during boot). So it will be obsolete once U-Boot
|
|
SPL is ported. And it is Yet Another Image Format. On the other hand, it
|
|
does work, and it is currently being used.
|
|
|
|
[1]: https://linux-sunxi.org/Allwinner_Nezha#U-Boot
|
|
[2]: https://fedoraproject.org/wiki/Architectures/RISC-V/Allwinner
|
|
[3]: https://github.com/smaeul/sun20i_d1_spl
|
|
END
|
|
Series-prefix: RFC
|
|
Series-to: sunxi
|
|
Signed-off-by: Samuel Holland <samuel@sholland.org>
|
|
---
|
|
boot/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
|
|
|
|
--- a/boot/image.c
|
|
+++ b/boot/image.c
|
|
@@ -180,6 +180,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, "", "", },
|
|
};
|
|
|
|
--- a/include/image.h
|
|
+++ b/include/image.h
|
|
@@ -229,6 +229,7 @@ enum image_type_t {
|
|
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 */
|
|
};
|
|
--- 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
|
|
--- 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-ccitt.o \
|
|
lib/hash-checksum.o \
|
|
lib/sha1.o \
|
|
--- /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
|
|
+);
|