Initial import master
authorAntonio Ospite <ao2@ao2.it>
Fri, 27 Mar 2015 22:04:24 +0000 (23:04 +0100)
committerAntonio Ospite <ao2@ao2.it>
Thu, 16 Apr 2015 14:53:07 +0000 (16:53 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
baytrail_sst_elf_firmware_convert.c [new file with mode: 0644]
baytrail_sst_firmware_parse.py [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ca1736e
--- /dev/null
@@ -0,0 +1,3 @@
+*~
+*.o
+baytrail_sst_elf_firmware_convert
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..4b535cf
--- /dev/null
@@ -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 (executable)
index 0000000..18b6d88
--- /dev/null
@@ -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)