From 1d569ed96005aea112a888987b53417018a6c4f1 Mon Sep 17 00:00:00 2001
From: Antonio Ospite <ao2@ao2.it>
Date: Fri, 27 Mar 2015 23:04:24 +0100
Subject: [PATCH] Initial import

---
 .gitignore                          |   3 +
 Makefile                            |  43 +++++
 baytrail_sst_elf_firmware_convert.c | 310 ++++++++++++++++++++++++++++++++++++
 baytrail_sst_firmware_parse.py      | 118 ++++++++++++++
 4 files changed, 474 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Makefile
 create mode 100644 baytrail_sst_elf_firmware_convert.c
 create mode 100755 baytrail_sst_firmware_parse.py

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ca1736e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.o
+baytrail_sst_elf_firmware_convert
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9ac258f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,43 @@
+CFLAGS ?= -std=c99 -pedantic -pedantic-errors -Wall -g3 -O2 -D_ANSI_SOURCE_
+CFLAGS += -fno-common \
+	  -Wall \
+	  -Wdeclaration-after-statement \
+	  -Wextra \
+	  -Wformat=2 \
+	  -Winit-self \
+	  -Winline \
+	  -Wpacked \
+	  -Wp,-D_FORTIFY_SOURCE=2 \
+	  -Wpointer-arith \
+	  -Wlarger-than-65500 \
+	  -Wmissing-declarations \
+	  -Wmissing-format-attribute \
+	  -Wmissing-noreturn \
+	  -Wmissing-prototypes \
+	  -Wnested-externs \
+	  -Wold-style-definition \
+	  -Wredundant-decls \
+	  -Wsign-compare \
+	  -Wstrict-aliasing=2 \
+	  -Wstrict-prototypes \
+	  -Wundef \
+	  -Wunreachable-code \
+	  -Wunused-variable \
+	  -Wwrite-strings
+
+ifneq ($(CC),clang)
+  CFLAGS += -Wunsafe-loop-optimizations
+endif
+
+# needed for htole32()
+CFLAGS += -D_BSD_SOURCE
+
+LDLIBS := -lelf
+
+baytrail_sst_elf_firmware_convert: baytrail_sst_elf_firmware_convert.o
+
+clean:
+	rm -f *~ *.o baytrail_sst_elf_firmware_convert
+
+test: baytrail_sst_elf_firmware_convert
+	valgrind --leak-check=full --show-reachable=yes ./baytrail_sst_elf_firmware_convert fw_sst_0f28.bin output.bin
diff --git a/baytrail_sst_elf_firmware_convert.c b/baytrail_sst_elf_firmware_convert.c
new file mode 100644
index 0000000..4b535cf
--- /dev/null
+++ b/baytrail_sst_elf_firmware_convert.c
@@ -0,0 +1,310 @@
+/* baytrail_sst_elf_firmware_convert - convert elf SST firmwares to new format
+ *
+ * Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <endian.h>
+
+#include <gelf.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/* SSP2 address */
+#define SST_BYT_IRAM_ADDRESS 0xff2c0000
+#define SST_BYT_DRAM_ADDRESS 0xff300000
+#define SST_BYT_DDR_ADDRESS 0xc0000000
+
+struct sst_ram_region {
+	const char *name;
+	uint32_t id;
+	uint32_t start_address;
+};
+
+static struct sst_ram_region ram_regions[] = {
+	{ "IRAM", 1, SST_BYT_IRAM_ADDRESS },
+	{ "DRAM", 2, SST_BYT_DRAM_ADDRESS },
+	{ "DDR", 5, SST_BYT_DDR_ADDRESS },
+};
+
+static inline void fwrite_le32(uint32_t x, FILE *file)
+{
+	uint32_t tmp = htole32(x);
+	fwrite(&tmp, sizeof(tmp), 1, file);
+}
+
+static struct sst_ram_region *phdr_to_sst_ram_region(GElf_Phdr *phdr)
+{
+	if (!phdr)
+		return NULL;
+
+	if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0) {
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(ram_regions); i++) {
+			struct sst_ram_region *r = &ram_regions[i];
+
+			if ((phdr->p_vaddr & r->start_address) == r->start_address)
+				return r;
+		}
+	}
+
+	return NULL;
+}
+
+/* 
+ * TODO: introduce data structures to represent the firmware headers and
+ * separate the firmware contruction from saving it to a file.
+ *
+ * This way the elf file can be scanned just once.
+ */
+static int convert_elf_to_sst_firmware(Elf *elf, FILE *output_file)
+{
+	int ret;
+	size_t num_phdrs = 0;
+	unsigned int phdr_index;
+	unsigned int num_sst_blocks;
+	unsigned int sst_blocks_total_size;
+	char *data;
+	size_t data_size = 0;
+
+	ret = elf_getphdrnum(elf, &num_phdrs);
+	if (ret < 0) {
+		fprintf(stderr, "Cannot get number of program headers: %s\n",
+			elf_errmsg(elf_errno()));
+		exit(EXIT_FAILURE);
+	}
+
+	/*
+	 * Perform a first scan just to see how many SST blocks there are and
+	 * how much space they take..
+	 *
+	 * Knowing this info before writing the firmware allows to write the
+	 * firmware incrementally without going back to amend the headers to
+	 * fill the number of blocks and the module and file sizes.
+	 */
+	num_sst_blocks = 0;
+	sst_blocks_total_size = 0;
+	for (phdr_index = 0; phdr_index < num_phdrs; ++phdr_index) {
+		void *retp;
+		GElf_Phdr phdr;
+		struct sst_ram_region *r;
+
+		retp = gelf_getphdr(elf, phdr_index, &phdr);
+		if (retp != &phdr) {
+			fprintf(stderr, "gelf_getphdr() failed: %s.",
+				elf_errmsg(elf_errno()));
+			exit(EXIT_FAILURE);
+		}
+
+		r = phdr_to_sst_ram_region(&phdr);
+		if (r) {
+			unsigned int pre_padding = 0;
+			num_sst_blocks++;
+			sst_blocks_total_size += phdr.p_filesz;
+
+#if 0
+			if ((phdr.p_vaddr % 16) != 0) {
+				pre_padding = 16 - (phdr.p_vaddr % 16);
+				fprintf(stderr, "pre_padding: %d\n", pre_padding);
+				sst_blocks_total_size += pre_padding;
+			}
+#endif
+
+			/* align to multiple of 4 */
+			if (((phdr.p_filesz + pre_padding) % 4) != 0) {
+				unsigned int post_padding = 4 - ((phdr.p_filesz + pre_padding) % 4);
+				fprintf(stderr, "filesz: %ld\n", phdr.p_filesz);
+				fprintf(stderr, "post_padding: %d\n", post_padding);
+				sst_blocks_total_size += post_padding;
+			}
+		}
+	}
+
+	fprintf(stderr, "Number of SST blocks: %d\n", num_sst_blocks);
+	fprintf(stderr, "SST blocks total size: %d\n", sst_blocks_total_size);
+
+	data = elf_rawfile(elf, &data_size);
+	if (data == NULL) {
+		fprintf(stderr, "Cannot get raw file contents: %s\n",
+			elf_errmsg(elf_errno()));
+		exit(EXIT_FAILURE);
+	}
+
+	/* start writing the SST firmware file */
+	fwrite("$SST\n", 1, 4, output_file);
+
+	/* file size: add blocks and module headers sizes */
+	fwrite_le32(sst_blocks_total_size + num_sst_blocks * 16 + 20, output_file);
+
+	/* num modules */
+	fwrite_le32(1, output_file);
+
+	/* file format */
+	fwrite_le32(256, output_file);
+
+	/* reserved */
+	fwrite_le32(0, output_file);
+	fwrite_le32(0, output_file);
+	fwrite_le32(0, output_file);
+	fwrite_le32(0, output_file);
+
+	/* start writing the SST module */
+	fwrite("$SST\n", 1, 4, output_file);
+
+	/* module size: add blocks headers sizes */
+	fwrite_le32(sst_blocks_total_size + num_sst_blocks * 16, output_file);
+
+	/* num blocks */
+	fwrite_le32(num_sst_blocks, output_file);
+
+	/* block type */
+	fwrite_le32(65535, output_file);
+
+	/* entry point */
+	fwrite_le32(0, output_file);
+
+	for (phdr_index = 0; phdr_index < num_phdrs; ++phdr_index) {
+		void *retp;
+		GElf_Phdr phdr;
+		struct sst_ram_region *r;
+
+		retp = gelf_getphdr(elf, phdr_index, &phdr);
+		if (retp != &phdr) {
+			fprintf(stderr, "gelf_getphdr() failed: %s.",
+				elf_errmsg(elf_errno()));
+			return -EINVAL;
+		}
+
+		r = phdr_to_sst_ram_region(&phdr);
+		if (r) {
+			uint32_t block_size;
+			uint32_t ram_offset;
+			unsigned int pre_padding = 0;
+			unsigned int post_padding = 0;
+			const uint8_t zero = 0;
+			unsigned int i;
+
+#if 0
+			if ((phdr.p_vaddr % 16) != 0)
+				pre_padding = 16 - (phdr.p_vaddr % 16);
+#endif
+
+			if (((phdr.p_filesz + pre_padding) % 4) != 0)
+				post_padding = 4 - ((phdr.p_filesz + pre_padding) % 4);
+
+			ram_offset = (phdr.p_vaddr & ~r->start_address) - pre_padding;
+			block_size = phdr.p_filesz + pre_padding + post_padding;
+
+			fprintf(stderr, "%s, id: %d, offset: 0x%08x\n", r->name, r->id, ram_offset);
+			fprintf(stderr, "vaddr: 0x%08lx paddr: 0x%08lx\n", phdr.p_vaddr, phdr.p_paddr);
+			fprintf(stderr, "alignment: %ld\n", phdr.p_align);
+			fprintf(stderr, "block_size: %d\n", block_size);
+
+			/* ram type */
+			fwrite_le32(r->id, output_file);
+
+			/* block size */
+			fwrite_le32(block_size, output_file);
+
+			/* ram offset */
+			fwrite_le32(ram_offset, output_file);
+
+			/* reserved */
+			fwrite_le32(0, output_file);
+
+			/* pre padding */
+			for (i = 0; i < pre_padding; i++)
+				fwrite(&zero, sizeof(zero), 1, output_file);
+
+			/* data */
+			fwrite(data + phdr.p_offset, phdr.p_filesz, 1, output_file);
+
+			/* post padding */
+			for (i = 0; i < post_padding; i++)
+				fwrite(&zero, sizeof(zero), 1, output_file);
+		}
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	int fd;
+	Elf *elf;
+	unsigned int version;
+	Elf_Kind kind;
+	FILE *output_file;
+	int ret;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s <elf_file> <output_file>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	version = elf_version(EV_CURRENT);
+	if (version == EV_NONE) {
+		fprintf(stderr, "Cannot initialize ELF library: %s\n",
+			elf_errmsg(elf_errno()));
+		exit(EXIT_FAILURE);
+	}
+
+	fd = open(argv[1], O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot open \"%s\": %s", argv[1],
+			strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	elf = elf_begin(fd, ELF_C_READ, NULL);
+	if (elf == NULL) {
+		fprintf(stderr, "Cannot initialize ELF descriptor: %s.",
+			elf_errmsg(elf_errno()));
+		exit(EXIT_FAILURE);
+	}
+
+	kind = elf_kind(elf);
+	if (kind != ELF_K_ELF) {
+		fprintf(stderr, "\"%s\" is not an ELF file.\n", argv[1]);
+		exit(EXIT_FAILURE);
+	}
+
+	output_file = fopen(argv[2], "wb");
+	if (output_file == NULL) {
+		fprintf(stderr, "Cannot open output file \"%s\": %s", argv[1],
+			strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	ret = convert_elf_to_sst_firmware(elf, output_file);
+	if (ret < 0) {
+		fprintf(stderr, "Cannot convert elf to SST firmware file\n");
+		exit(EXIT_FAILURE);
+	}
+
+	elf_end(elf);
+	close(fd);
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/baytrail_sst_firmware_parse.py b/baytrail_sst_firmware_parse.py
new file mode 100755
index 0000000..18b6d88
--- /dev/null
+++ b/baytrail_sst_firmware_parse.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+#
+# baytrail_sst_firmware_parse.py - parse an SST firmware file
+#
+# Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software. It comes without any warranty, to
+# the extent permitted by applicable law. You can redistribute it
+# and/or modify it under the terms of the Do What The Fuck You Want
+# To Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+
+import os
+import sys
+import struct
+
+FIRMWARE_HEADER_SIZE = 32
+MODULE_HEADER_SIZE = 20
+
+
+def get(f, fmt, offset=None):
+    if offset:
+        f.seek(offset)
+
+    length = struct.calcsize(fmt)
+    data = f.read(length)
+    value = struct.unpack_from(fmt, data)
+    return value[0]
+
+
+def sst_firmware_parse(firmware_file_name):
+
+    f = open(firmware_file_name, "rb")
+    filesize = os.path.getsize(firmware_file_name)
+
+    signature = get(f, "4s")
+    if signature != "$SST":
+        print "Invalid file"
+        return False
+
+    payload_size = get(f, '<L')
+    print "Payload size:", payload_size
+
+    if (payload_size + FIRMWARE_HEADER_SIZE) != filesize:
+        print "invalid payload size"
+        return False
+
+    num_modules = get(f, '<L')
+    print "Number of modules:", num_modules
+
+    file_format = get(f, '<L')
+    print "File format:", file_format
+
+    # reserved fields
+    get(f, '<L')
+    get(f, '<L')
+    get(f, '<L')
+    get(f, '<L')
+
+    for i in range(0, num_modules):
+        print "\nModule %d" % i
+
+        signature = get(f, "4s")
+        if signature != "$SST":
+            print "Invalid module"
+            return False
+
+        module_size = get(f, '<L')
+        print "module size:", module_size
+
+        if (module_size + MODULE_HEADER_SIZE) != payload_size:
+            print "Invalid module size:"
+            return False
+
+        num_blocks = get(f, '<L')
+        print "Number of blocks:", num_blocks
+
+        block_type = get(f, '<L')
+        print "Block type: ", block_type
+
+        entry_point = get(f, '<L')
+        print "Entry point: 0x%08x" % entry_point
+
+        total_block_size = 0
+        for j in range(0, num_blocks):
+            print "\nBlock %d" % j
+
+            ram_type = get(f, '<L')
+            print "Ram type:", ram_type
+
+            block_size = get(f, '<L')
+            print "Block size: %d (0x%08x)" % (block_size, block_size)
+
+            total_block_size += block_size + 16
+            print "Total block size: %d (0x%08x)" % \
+                (total_block_size, total_block_size)
+
+            ram_offset = get(f, '<L')
+            print "Ram offset: 0x%08x" % ram_offset
+
+            # reserved
+            get(f, '<L')
+
+            # actual data
+            get(f, "%db" % block_size)
+
+        if total_block_size != module_size:
+            print "Invalid total block size:"
+            return False
+
+    return True
+
+
+if __name__ == "__main__":
+    filename = sys.argv[1]
+    ret = sst_firmware_parse(filename)
+
+    sys.exit(ret)
-- 
2.1.4