From 590c7825c191800b98cf00a431e95bba07c64604 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Mon, 1 Feb 2016 12:42:00 +0000
Subject: firmware-utils: add oseama tool for creating Seama entities
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

It has an important feature (compared to seama) of using multiple input
files, aligning them and padding zeroes until reaching a specified
absolute offset. This is needed for a proper flash layout on NAND. We
want kernel partition to be big enough to handle future updates without
a need to resize it and wipe whole "ubi" partition. It's important as
we don't want to lose block counters.

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>

SVN-Revision: 48601
---
 tools/firmware-utils/Makefile     |   1 +
 tools/firmware-utils/src/oseama.c | 422 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 423 insertions(+)
 create mode 100644 tools/firmware-utils/src/oseama.c

(limited to 'tools')

diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
index dc922b0360..7a4403cba2 100644
--- a/tools/firmware-utils/Makefile
+++ b/tools/firmware-utils/Makefile
@@ -64,6 +64,7 @@ define Host/Compile
 	$(call cc,mkdapimg)
 	$(call cc, mkcameofw, -Wall)
 	$(call cc,seama md5)
+	$(call cc,oseama md5, -Wall)
 	$(call cc,fix-u-media-header cyg_crc32,-Wall)
 	$(call cc,hcsmakeimage bcmalgo)
 	$(call cc,mkporayfw, -Wall)
diff --git a/tools/firmware-utils/src/oseama.c b/tools/firmware-utils/src/oseama.c
new file mode 100644
index 0000000000..37a2171574
--- /dev/null
+++ b/tools/firmware-utils/src/oseama.c
@@ -0,0 +1,422 @@
+/*
+ * oseama
+ *
+ * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <byteswap.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "md5.h"
+
+#if !defined(__BYTE_ORDER)
+#error "Unknown byte order"
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_be32(x)	(x)
+#define be32_to_cpu(x)	(x)
+#define cpu_to_be16(x)	(x)
+#define be16_to_cpu(x)	(x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_be32(x)	bswap_32(x)
+#define be32_to_cpu(x)	bswap_32(x)
+#define cpu_to_be16(x)	bswap_16(x)
+#define be16_to_cpu(x)	bswap_16(x)
+#else
+#error "Unsupported endianness"
+#endif
+
+#define SEAMA_MAGIC			0x5ea3a417
+
+struct seama_seal_header {
+	uint32_t magic;
+	uint16_t reserved;
+	uint16_t metasize;
+	uint32_t imagesize;
+} __attribute__ ((packed));
+
+struct seama_entity_header {
+	uint32_t magic;
+	uint16_t reserved;
+	uint16_t metasize;
+	uint32_t imagesize;
+	uint8_t md5[16];
+} __attribute__ ((packed));
+
+char *seama_path;
+int entity_idx = -1;
+
+static inline size_t oseama_min(size_t x, size_t y) {
+	return x < y ? x : y;
+}
+
+/**************************************************
+ * Info
+ **************************************************/
+
+static void oseama_info_parse_options(int argc, char **argv) {
+	int c;
+
+	while ((c = getopt(argc, argv, "e:")) != -1) {
+		switch (c) {
+		case 'e':
+			entity_idx = atoi(optarg);
+			break;
+		}
+	}
+}
+
+static int oseama_info_entities(FILE *seama) {
+	struct seama_entity_header hdr;
+	size_t bytes, metasize, imagesize;
+	uint8_t buf[1024];
+	char *end, *tmp;
+	int i = 0;
+	int err = 0;
+
+	while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) {
+		if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
+			fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
+			err =  -EINVAL;
+			goto err_out;
+		}
+		metasize = be16_to_cpu(hdr.metasize);
+		imagesize = be32_to_cpu(hdr.imagesize);
+
+		if (entity_idx >= 0 && i != entity_idx) {
+			fseek(seama, metasize + imagesize, SEEK_CUR);
+			i++;
+			continue;
+		}
+
+		if (metasize >= sizeof(buf)) {
+			fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize);
+			err =  -EINVAL;
+			goto err_out;
+		}
+
+		if (entity_idx < 0)
+			printf("\n");
+		printf("Entity offset:\t%ld\n", ftell(seama) - sizeof(hdr));
+		printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize);
+		printf("Meta size:\t%zd\n", metasize);
+		printf("Image size:\t%zd\n", imagesize);
+
+		bytes = fread(buf, 1, metasize, seama);
+		if (bytes != metasize) {
+			fprintf(stderr, "Couldn't read %zd B of meta\n", metasize);
+			err =  -EIO;
+			goto err_out;
+		}
+
+		end = (char *)&buf[metasize - 1];
+		*end = '\0';
+		for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
+			printf("Meta entry:\t%s\n", tmp);
+		}
+
+		fseek(seama, imagesize, SEEK_CUR);
+		i++;
+	}
+
+err_out:
+	return err;
+}
+
+static int oseama_info(int argc, char **argv) {
+	FILE *seama;
+	struct seama_seal_header hdr;
+	size_t bytes;
+	uint16_t metasize;
+	uint32_t imagesize;
+	uint8_t buf[1024];
+	int err = 0;
+
+	if (argc < 3) {
+		fprintf(stderr, "No Seama file passed\n");
+		err = -EINVAL;
+		goto out;
+	}
+	seama_path = argv[2];
+
+	optind = 3;
+	oseama_info_parse_options(argc, argv);
+
+	seama = fopen(seama_path, "r");
+	if (!seama) {
+		fprintf(stderr, "Couldn't open %s\n", seama_path);
+		err = -EACCES;
+		goto out;
+	}
+
+	bytes = fread(&hdr, 1, sizeof(hdr), seama);
+	if (bytes != sizeof(hdr)) {
+		fprintf(stderr, "Couldn't read %s header\n", seama_path);
+		err =  -EIO;
+		goto err_close;
+	}
+	metasize = be16_to_cpu(hdr.metasize);
+	imagesize = be32_to_cpu(hdr.imagesize);
+
+	if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
+		fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
+		err =  -EINVAL;
+		goto err_close;
+	}
+
+	if (metasize >= sizeof(buf)) {
+		fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize);
+		err =  -EINVAL;
+		goto err_close;
+	}
+
+	if (imagesize) {
+		fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize);
+		err =  -EINVAL;
+		goto err_close;
+	}
+
+	bytes = fread(buf, 1, metasize, seama);
+	if (bytes != metasize) {
+		fprintf(stderr, "Couldn't read %d B of meta\n", metasize);
+		err =  -EIO;
+		goto err_close;
+	}
+
+	if (entity_idx < 0) {
+		char *end, *tmp;
+
+		printf("Meta size:\t%d\n", metasize);
+		printf("Image size:\t%d\n", imagesize);
+
+		end = (char *)&buf[metasize - 1];
+		*end = '\0';
+		for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
+			printf("Meta entry:\t%s\n", tmp);
+		}
+	}
+
+	oseama_info_entities(seama);
+
+err_close:
+	fclose(seama);
+out:
+	return err;
+}
+
+/**************************************************
+ * Create
+ **************************************************/
+
+static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) {
+	FILE *in;
+	size_t bytes;
+	ssize_t length = 0;
+	uint8_t buf[128];
+
+	in = fopen(in_path, "r");
+	if (!in) {
+		fprintf(stderr, "Couldn't open %s\n", in_path);
+		return -EACCES;
+	}
+
+	while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
+		if (fwrite(buf, 1, bytes, seama) != bytes) {
+			fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path);
+			length = -EIO;
+			break;
+		}
+		length += bytes;
+	}
+
+	fclose(in);
+
+	return length;
+}
+
+static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) {
+	uint8_t *buf;
+
+	buf = malloc(length);
+	if (!buf)
+		return -ENOMEM;
+	memset(buf, 0, length);
+
+	if (fwrite(buf, 1, length, seama) != length) {
+		fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path);
+		return -EIO;
+	}
+
+	return length;
+}
+
+static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) {
+	if (curr_offset & (alignment - 1)) {
+		size_t length = alignment - (curr_offset % alignment);
+
+		return oseama_entity_append_zeros(seama, length);
+	}
+
+	return 0;
+}
+
+static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) {
+	struct seama_entity_header hdr = {};
+	uint8_t buf[128];
+	size_t length = imagesize;
+	size_t bytes;
+	MD5_CTX ctx;
+
+	fseek(seama, sizeof(hdr) + metasize, SEEK_SET);
+	MD5_Init(&ctx);
+	while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) {
+		MD5_Update(&ctx, buf, bytes);
+		length -= bytes;
+	}
+	MD5_Final(hdr.md5, &ctx);
+
+	hdr.magic = cpu_to_be32(SEAMA_MAGIC);
+	hdr.metasize = cpu_to_be16(metasize);
+	hdr.imagesize = cpu_to_be32(imagesize);
+
+	fseek(seama, 0, SEEK_SET);
+	bytes = fwrite(&hdr, 1, sizeof(hdr), seama);
+	if (bytes != sizeof(hdr)) {
+		fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int oseama_entity(int argc, char **argv) {
+	FILE *seama;
+	ssize_t sbytes;
+	size_t curr_offset = sizeof(struct seama_entity_header);
+	size_t metasize = 0, imagesize = 0;
+	int c;
+	int err = 0;
+
+	if (argc < 3) {
+		fprintf(stderr, "No Seama file passed\n");
+		err = -EINVAL;
+		goto out;
+	}
+	seama_path = argv[2];
+
+	seama = fopen(seama_path, "w+");
+	if (!seama) {
+		fprintf(stderr, "Couldn't open %s\n", seama_path);
+		err = -EACCES;
+		goto out;
+	}
+	fseek(seama, curr_offset, SEEK_SET);
+
+	optind = 3;
+	while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
+		switch (c) {
+		case 'm':
+			sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama);
+			if (sbytes < 0) {
+				fprintf(stderr, "Failed to write meta %s\n", optarg);
+			} else {
+				curr_offset += sbytes;
+				metasize += sbytes;
+			}
+
+			sbytes = oseama_entity_align(seama, curr_offset, 4);
+			if (sbytes < 0) {
+				fprintf(stderr, "Failed to append zeros\n");
+			} else {
+				curr_offset += sbytes;
+				metasize += sbytes;
+			}
+
+			break;
+		case 'f':
+		case 'b':
+			break;
+		}
+	}
+
+	optind = 3;
+	while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
+		switch (c) {
+		case 'm':
+			break;
+		case 'f':
+			sbytes = oseama_entity_append_file(seama, optarg);
+			if (sbytes < 0) {
+				fprintf(stderr, "Failed to append file %s\n", optarg);
+			} else {
+				curr_offset += sbytes;
+				imagesize += sbytes;
+			}
+			break;
+		case 'b':
+			sbytes = strtol(optarg, NULL, 0) - curr_offset;
+			if (sbytes < 0) {
+				fprintf(stderr, "Current Seama entity length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
+			} else {
+				sbytes = oseama_entity_append_zeros(seama, sbytes);
+				if (sbytes < 0) {
+					fprintf(stderr, "Failed to append zeros\n");
+				} else {
+					curr_offset += sbytes;
+					imagesize += sbytes;
+				}
+			}
+			break;
+		}
+		if (err)
+			break;
+	}
+
+	oseama_entity_write_hdr(seama, metasize, imagesize);
+
+	fclose(seama);
+out:
+	return err;
+}
+
+/**************************************************
+ * Start
+ **************************************************/
+
+static void usage() {
+	printf("Usage:\n");
+	printf("\n");
+	printf("Info about Seama seal (container):\n");
+	printf("\toseama info <file> [options]\n");
+	printf("\t-e\t\t\t\tprint info about specified entity only\n");
+	printf("\n");
+	printf("Create Seama entity:\n");
+	printf("\toseama entity <file> [options]\n");
+	printf("\t-m meta\t\t\t\tmeta into to put in header\n");
+	printf("\t-f file\t\t\t\tappend content from file\n");
+	printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n");
+}
+
+int main(int argc, char **argv) {
+	if (argc > 1) {
+		if (!strcmp(argv[1], "info"))
+			return oseama_info(argc, argv);
+		else if (!strcmp(argv[1], "entity"))
+			return oseama_entity(argc, argv);
+	}
+
+	usage();
+	return 0;
+}
-- 
cgit v1.2.3