/* 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); }