From 1d569ed96005aea112a888987b53417018a6c4f1 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Fri, 27 Mar 2015 23:04:24 +0100 Subject: [PATCH 1/1] 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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 \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 +# +# 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, '