[RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images

Ralph Siemsen
2022-08-09T13:02:11Z

From: Michel Pollet <michel.pollet@bp.renesas.com>

Renesas RZ/N1 devices contain BootROM code that loads a custom SPKG
image from QSPI, NAND or USB DFU. This tool converts a binary image into
an SPKG.

SPKGs can optionally be signed, however this tool does not currently
support signed SPKGs.

Signed-off-by: Michel Pollet <michel.pollet@bp.renesas.com>
Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
This tool could possibly be incorporated into mkimage / imagetools.
However it is unclear how to handle the extra commandline parameters
(NAND ECC settings, etc). So for now it is stand-alone tool.

tools/Makefile | 2 +
tools/spkg_header.h | 49 +++++++
tools/spkg_utility.c | 306 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 357 insertions(+)
create mode 100644 tools/spkg_header.h
create mode 100644 tools/spkg_utility.c

diff --git a/tools/Makefile b/tools/Makefile
index 005e7362a3..c5dc92a13f 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -68,6 +68,8 @@ HOSTCFLAGS_img2srec.o := -pedantic
hostprogs-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes
HOSTCFLAGS_xway-swap-bytes.o := -pedantic

+hostprogs-$(CONFIG_ARCH_RZN1) += spkg_utility
+
hostprogs-y += mkenvimage
mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o

diff --git a/tools/spkg_header.h b/tools/spkg_header.h
new file mode 100644
index 0000000000..029930cbf8
--- /dev/null
+++ b/tools/spkg_header.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Renesas RZ/N1 Linux tools: Package Table format
+ * (C) 2015-2016 Renesas Electronics Europe, LTD
+ * All rights reserved.
+ */
+
+#ifndef _SKGT_HEADER_H_
+#define _SKGT_HEADER_H_
+
+#define SPKG_HEADER_SIGNATURE (('R'<<0)|('Z'<<8)|('N'<<16)|('1'<<24))
+#define SPKG_HEADER_COUNT 8
+#define SPKG_BLP_SIZE 264
+
+#define SPKG_HEADER_SIZE 24
+#define SPKG_HEADER_SIZE_ALL (SPKG_HEADER_SIZE * SPKG_HEADER_COUNT)
+#define SPKG_HEADER_CRC_SIZE 4
+
+/* Index into SPKG */
+#define INDEX_BLP_START SPKG_HEADER_SIZE_ALL
+#define INDEX_IMAGE_START (INDEX_BLP_START + SPKG_BLP_SIZE)
+
+/* Flags, not supported by ROM code, only used for U-Boot SPL */
+enum {
+ SPKG_CODE_NONSEC_BIT = 0,
+ SPKG_CODE_HYP_BIT,
+};
+
+/* SPKG header */
+struct spkg_hdr {
+ uint32_t signature;
+ uint8_t version;
+ uint8_t ecc;
+ uint8_t ecc_scheme;
+ uint8_t ecc_bytes;
+ uint32_t payload_length; /* only HIGHER 24 bits */
+ uint32_t load_address;
+ uint32_t execution_offset;
+ uint32_t crc; /* of this header */
+} __attribute__((packed));
+
+struct spkg_file {
+ struct spkg_hdr header[SPKG_HEADER_COUNT];
+ uint8_t blp[SPKG_BLP_SIZE];
+ uint8_t data[0];
+ /* then the CRC */
+} __attribute__((packed));
+
+#endif
diff --git a/tools/spkg_utility.c b/tools/spkg_utility.c
new file mode 100644
index 0000000000..235e23e0f1
--- /dev/null
+++ b/tools/spkg_utility.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * This is a utility to create a SPKG file.
+ * It packages the binary code into the SPKG.
+ *
+ * (C) Copyright 2016 Renesas Electronics Europe Ltd
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "spkg_header.h"
+
+/* For Windows compatibility */
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ #define htole32(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ #define htole32(x) __builtin_bswap32((uint32_t)(x))
+#endif
+#endif
+
+#define MAX_PATH 300
+
+// Note: Order of bit fields is not used, this is purely just holding the SPKG information.
+struct spkg_header {
+ char input[MAX_PATH];
+ char output[MAX_PATH + 5];
+ unsigned int version:4;
+ unsigned int ecc_enable:1;
+ unsigned int ecc_block_size:2;
+ unsigned int ecc_scheme:3;
+ unsigned int ecc_bytes:8;
+ unsigned int dummy_blp_length:10;
+ unsigned int payload_length:24;
+ unsigned int spl_nonsec:1;
+ unsigned int spl_hyp:1;
+ unsigned int load_address;
+ unsigned int execution_offset;
+ uint32_t padding;
+};
+
+struct spkg_header g_header = {
+ .version = 1,
+ .padding = 256,
+};
+
+int verbose;
+
+static uint32_t crc32(const uint8_t *message, uint32_t l)
+{
+ uint32_t crc = ~0;
+
+ while (l--) {
+ uint32_t byte = *message++; // Get next byte.
+
+ crc = crc ^ byte;
+ for (int8_t j = 7; j >= 0; j--) { // Do eight times.
+ uint32_t mask = -(crc & 1);
+
+ crc = (crc >> 1) ^ (0xEDB88320 & mask);
+ }
+ }
+ return ~crc;
+}
+
+static int spkg_write(struct spkg_header *h, FILE *file_input, FILE *file_SPKG)
+{
+ int i;
+ uint32_t length_inputfile;
+ uint32_t length_read;
+ uint32_t length_written;
+ uint32_t length_total;
+ uint32_t padding = 0;
+ uint8_t *data, *start;
+ uint32_t crc;
+
+ /* Calculate length of input file */
+ fseek(file_input, 0, SEEK_END); // seek to end of file
+ length_inputfile = ftell(file_input); // get current file pointer
+ fseek(file_input, 0, SEEK_SET); // seek back to beginning of file
+
+ /* Set payload_length field. */
+ h->payload_length =
+ length_inputfile + h->dummy_blp_length + SPKG_HEADER_CRC_SIZE;
+
+ /* Calculate total length of SPKG */
+ length_total =
+ (SPKG_HEADER_SIZE * SPKG_HEADER_COUNT) + h->dummy_blp_length +
+ length_inputfile + SPKG_HEADER_CRC_SIZE;
+ padding = h->padding ? h->padding - (length_total % h->padding) : 0;
+ length_total += padding;
+ /* Padding needs to be part of the payload size, otherwise the ROM DFU
+ * refuses to accept the extra bytes and return and error.
+ */
+ h->payload_length += padding;
+
+ printf("Addr: 0x%08x ", h->load_address);
+ printf("In: %8d ", length_inputfile);
+ printf("padding to %3dKB: %6d ", h->padding / 1024, padding);
+ printf("Total: 0x%08x ", length_total);
+ printf("%s\n", h->output);
+
+ /* Create and zero array for SPKG */
+ data = malloc(length_total);
+ memset(data, 0, length_total);
+
+ /* Fill the SPKG with the headers */
+ {
+ struct spkg_hdr head = {
+ .signature = SPKG_HEADER_SIGNATURE,
+ .version = h->version,
+ .ecc = (h->ecc_enable << 5) | (h->ecc_block_size << 1),
+ .ecc_scheme = h->ecc_scheme,
+ .ecc_bytes = h->ecc_bytes,
+ .payload_length = htole32((h->payload_length << 8) |
+ (h->spl_nonsec << SPKG_CODE_NONSEC_BIT) |
+ (h->spl_hyp << SPKG_CODE_HYP_BIT)),
+ .load_address = htole32(h->load_address),
+ .execution_offset = htole32(h->execution_offset),
+ };
+
+ head.crc = crc32((uint8_t *)&head, sizeof(head) - SPKG_HEADER_CRC_SIZE);
+ for (i = 0; i < SPKG_HEADER_COUNT; i++)
+ ((struct spkg_hdr *)data)[i] = head;
+ }
+
+ start = data + INDEX_BLP_START;
+
+ /* Fill the SPKG with the Dummy BLp */
+ for (i = 0; i < h->dummy_blp_length; i++)
+ *start++ = 0x88;
+ /* Fill the SPKG with the data from the code file. */
+ length_read =
+ fread(start, sizeof(char), length_inputfile,
+ file_input);
+
+ if (length_read != length_inputfile) {
+ fprintf(stderr, "Error reading %s: ferror=%d, feof=%d\n",
+ h->input, ferror(file_input), feof(file_input));
+
+ return -1;
+ }
+ /* fill padding with flash friendly one bits */
+ memset(start + length_inputfile + SPKG_HEADER_CRC_SIZE, 0xff, padding);
+
+ /* Add Payload CRC */
+ crc = crc32(&data[INDEX_BLP_START],
+ h->dummy_blp_length + length_inputfile + padding);
+
+ start += length_inputfile + padding;
+ start[0] = crc;
+ start[1] = crc >> 8;
+ start[2] = crc >> 16;
+ start[3] = crc >> 24;
+
+ /* Write the completed SKPG to file */
+ length_written = fwrite(data, sizeof(char), length_total, file_SPKG);
+
+ if (length_written != length_total) {
+ fprintf(stderr, "Error writing to %s\n", h->output);
+ return -1;
+ }
+
+ return 0;
+}
+
+const char *usage =
+ "%s\n"
+ " [-i <filename>] : input file\n"
+ " [-o <filename>] : output file\n"
+ " [--load_address <hex constant>] : code load address\n"
+ " [--execution_offset <hex constant>] : starting offset\n"
+ " [--nand_ecc_enable] : Enable nand ECC\n"
+ " [--nand_ecc_blksize <hex constant>] : Block size code\n"
+ " 0=256 bytes, 1=512 bytes, 2=1024 bytes\n"
+ " [--nand_ecc_scheme <hex constant>] : ECC scheme code\n"
+ " 0=BCH2 1=BCH4 2=BCH8 3=BCH16 4=BCH24 5=BCH32\n"
+ " [--add_dummy_blp] : Add a passthru BLP\n"
+ " [--spl_nonsec] : Code package run in NONSEC\n"
+ " [--spl_hyp] : Code package run in HYP (and NONSEC)\n"
+ " [--padding <value>[K|M]] : Pass SPKG to <value> size block\n"
+ ;
+
+static int spkg_parse_option(struct spkg_header *h, const char *name,
+ const char *sz, uint32_t value)
+{
+// printf("%s %s=%s %08x\n", __func__, name, sz, value);
+ if (!strcmp("file_input", name) || !strcmp("i", name)) {
+ strncpy(h->input, sz, sizeof(h->input) - 1);
+ h->input[sizeof(h->input) - 1] = 0;
+ } else if (!strcmp("file_output", name) || !strcmp("o", name)) {
+ strncpy(h->output, sz, sizeof(h->output) - 1);
+ h->output[sizeof(h->output) - 1] = 0;
+ } else if (!strcmp("version", name)) {
+ h->version = value;
+ } else if (!strcmp("load_address", name)) {
+ h->load_address = value;
+ } else if (!strcmp("execution_offset", name)) {
+ h->execution_offset = value;
+ } else if (!strcmp("nand_ecc_enable", name)) {
+ h->ecc_enable = value;
+ } else if (!strcmp("nand_ecc_blksize", name)) {
+ h->ecc_block_size = value;
+ } else if (!strcmp("nand_ecc_scheme", name)) {
+ h->ecc_scheme = value;
+ } else if (!strcmp("nand_bytes_per_ecc_block", name)) {
+ h->ecc_bytes = value;
+ } else if (!strcmp("add_dummy_blp", name)) {
+ h->dummy_blp_length = value ? SPKG_BLP_SIZE : 0;
+ } else if (!strcmp("spl_nonsec", name)) {
+ h->spl_nonsec = !!value;
+ } else if (!strcmp("spl_hyp", name)) {
+ h->spl_hyp = !!value;
+ } else if (!strcmp("help", name) || !strcmp("h", name)) {
+ fprintf(stderr, usage, "spkg_utility");
+ exit(0);
+ } else if (!strcmp("padding", name) && sz && value) {
+ if (strchr(sz, 'K'))
+ h->padding = value * 1024;
+ else if (strchr(sz, 'M'))
+ h->padding = value * 1024 * 1024;
+ else
+ h->padding = value;
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *file_SPKG = NULL;
+ FILE *file_input = NULL;
+ int result = -1;
+
+ for (int i = 1; i < argc; i++) {
+ unsigned long value = 0;
+ char *name = argv[i];
+ char *sz = NULL;
+
+ if (!name || name[0] != '-') {
+ fprintf(stderr, "%s invalid argument '%s'\n",
+ argv[0], argv[i]);
+ return -1;
+ }
+ name++;
+ if (name[0] == '-')
+ name++;
+
+ if (i < argc - 1 && argv[i + 1][0] != '-')
+ sz = argv[++i];
+
+ if (sz) {
+ if (!sscanf(sz, "0x%lx", &value))
+ sscanf(sz, "%lu", &value);
+ } else {
+ value = 1;
+ }
+ if (spkg_parse_option(&g_header, name, sz, value)) {
+ fprintf(stderr, "%s Error invalid '%s'\n",
+ argv[0], argv[i]);
+ return -1;
+ }
+ }
+
+ if (!g_header.input[0]) {
+ fprintf(stderr, usage, argv[0]);
+ exit(1);
+ }
+ if (!g_header.output[0])
+ snprintf(g_header.output, sizeof(g_header.output),
+ "%s.spkg", g_header.input);
+ if (verbose)
+ printf("%s -> %s\n", g_header.input, g_header.output);
+
+ /*NOTE: Using binary mode as this seems necessary if running in Windows */
+ file_SPKG = fopen(g_header.output, "wb");
+ file_input = fopen(g_header.input, "rb");
+
+ if (!file_SPKG)
+ perror(g_header.output);
+ if (!file_input)
+ perror(g_header.input);
+
+ if (file_input && file_SPKG)
+ result = spkg_write(&g_header, file_input, file_SPKG);
+
+ if (file_SPKG)
+ fclose(file_SPKG);
+ if (file_input)
+ fclose(file_input);
+
+ if (result >= 0) {
+ if (verbose)
+ printf("%s created\n", g_header.output);
+ } else {
+ fprintf(stderr, "ERROR creating %s\n", g_header.output);
+ }
+
+ return result;
+}
--
2.25.1