Initial import
[sst_elf_firmware_convert.git] / baytrail_sst_elf_firmware_convert.c
1 /* baytrail_sst_elf_firmware_convert - convert elf SST firmwares to new format
2  *
3  * Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <endian.h>
28
29 #include <gelf.h>
30
31 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
32
33 /* SSP2 address */
34 #define SST_BYT_IRAM_ADDRESS 0xff2c0000
35 #define SST_BYT_DRAM_ADDRESS 0xff300000
36 #define SST_BYT_DDR_ADDRESS 0xc0000000
37
38 struct sst_ram_region {
39         const char *name;
40         uint32_t id;
41         uint32_t start_address;
42 };
43
44 static struct sst_ram_region ram_regions[] = {
45         { "IRAM", 1, SST_BYT_IRAM_ADDRESS },
46         { "DRAM", 2, SST_BYT_DRAM_ADDRESS },
47         { "DDR", 5, SST_BYT_DDR_ADDRESS },
48 };
49
50 static inline void fwrite_le32(uint32_t x, FILE *file)
51 {
52         uint32_t tmp = htole32(x);
53         fwrite(&tmp, sizeof(tmp), 1, file);
54 }
55
56 static struct sst_ram_region *phdr_to_sst_ram_region(GElf_Phdr *phdr)
57 {
58         if (!phdr)
59                 return NULL;
60
61         if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0) {
62                 unsigned int i;
63
64                 for (i = 0; i < ARRAY_SIZE(ram_regions); i++) {
65                         struct sst_ram_region *r = &ram_regions[i];
66
67                         if ((phdr->p_vaddr & r->start_address) == r->start_address)
68                                 return r;
69                 }
70         }
71
72         return NULL;
73 }
74
75 /* 
76  * TODO: introduce data structures to represent the firmware headers and
77  * separate the firmware contruction from saving it to a file.
78  *
79  * This way the elf file can be scanned just once.
80  */
81 static int convert_elf_to_sst_firmware(Elf *elf, FILE *output_file)
82 {
83         int ret;
84         size_t num_phdrs = 0;
85         unsigned int phdr_index;
86         unsigned int num_sst_blocks;
87         unsigned int sst_blocks_total_size;
88         char *data;
89         size_t data_size = 0;
90
91         ret = elf_getphdrnum(elf, &num_phdrs);
92         if (ret < 0) {
93                 fprintf(stderr, "Cannot get number of program headers: %s\n",
94                         elf_errmsg(elf_errno()));
95                 exit(EXIT_FAILURE);
96         }
97
98         /*
99          * Perform a first scan just to see how many SST blocks there are and
100          * how much space they take..
101          *
102          * Knowing this info before writing the firmware allows to write the
103          * firmware incrementally without going back to amend the headers to
104          * fill the number of blocks and the module and file sizes.
105          */
106         num_sst_blocks = 0;
107         sst_blocks_total_size = 0;
108         for (phdr_index = 0; phdr_index < num_phdrs; ++phdr_index) {
109                 void *retp;
110                 GElf_Phdr phdr;
111                 struct sst_ram_region *r;
112
113                 retp = gelf_getphdr(elf, phdr_index, &phdr);
114                 if (retp != &phdr) {
115                         fprintf(stderr, "gelf_getphdr() failed: %s.",
116                                 elf_errmsg(elf_errno()));
117                         exit(EXIT_FAILURE);
118                 }
119
120                 r = phdr_to_sst_ram_region(&phdr);
121                 if (r) {
122                         unsigned int pre_padding = 0;
123                         num_sst_blocks++;
124                         sst_blocks_total_size += phdr.p_filesz;
125
126 #if 0
127                         if ((phdr.p_vaddr % 16) != 0) {
128                                 pre_padding = 16 - (phdr.p_vaddr % 16);
129                                 fprintf(stderr, "pre_padding: %d\n", pre_padding);
130                                 sst_blocks_total_size += pre_padding;
131                         }
132 #endif
133
134                         /* align to multiple of 4 */
135                         if (((phdr.p_filesz + pre_padding) % 4) != 0) {
136                                 unsigned int post_padding = 4 - ((phdr.p_filesz + pre_padding) % 4);
137                                 fprintf(stderr, "filesz: %ld\n", phdr.p_filesz);
138                                 fprintf(stderr, "post_padding: %d\n", post_padding);
139                                 sst_blocks_total_size += post_padding;
140                         }
141                 }
142         }
143
144         fprintf(stderr, "Number of SST blocks: %d\n", num_sst_blocks);
145         fprintf(stderr, "SST blocks total size: %d\n", sst_blocks_total_size);
146
147         data = elf_rawfile(elf, &data_size);
148         if (data == NULL) {
149                 fprintf(stderr, "Cannot get raw file contents: %s\n",
150                         elf_errmsg(elf_errno()));
151                 exit(EXIT_FAILURE);
152         }
153
154         /* start writing the SST firmware file */
155         fwrite("$SST\n", 1, 4, output_file);
156
157         /* file size: add blocks and module headers sizes */
158         fwrite_le32(sst_blocks_total_size + num_sst_blocks * 16 + 20, output_file);
159
160         /* num modules */
161         fwrite_le32(1, output_file);
162
163         /* file format */
164         fwrite_le32(256, output_file);
165
166         /* reserved */
167         fwrite_le32(0, output_file);
168         fwrite_le32(0, output_file);
169         fwrite_le32(0, output_file);
170         fwrite_le32(0, output_file);
171
172         /* start writing the SST module */
173         fwrite("$SST\n", 1, 4, output_file);
174
175         /* module size: add blocks headers sizes */
176         fwrite_le32(sst_blocks_total_size + num_sst_blocks * 16, output_file);
177
178         /* num blocks */
179         fwrite_le32(num_sst_blocks, output_file);
180
181         /* block type */
182         fwrite_le32(65535, output_file);
183
184         /* entry point */
185         fwrite_le32(0, output_file);
186
187         for (phdr_index = 0; phdr_index < num_phdrs; ++phdr_index) {
188                 void *retp;
189                 GElf_Phdr phdr;
190                 struct sst_ram_region *r;
191
192                 retp = gelf_getphdr(elf, phdr_index, &phdr);
193                 if (retp != &phdr) {
194                         fprintf(stderr, "gelf_getphdr() failed: %s.",
195                                 elf_errmsg(elf_errno()));
196                         return -EINVAL;
197                 }
198
199                 r = phdr_to_sst_ram_region(&phdr);
200                 if (r) {
201                         uint32_t block_size;
202                         uint32_t ram_offset;
203                         unsigned int pre_padding = 0;
204                         unsigned int post_padding = 0;
205                         const uint8_t zero = 0;
206                         unsigned int i;
207
208 #if 0
209                         if ((phdr.p_vaddr % 16) != 0)
210                                 pre_padding = 16 - (phdr.p_vaddr % 16);
211 #endif
212
213                         if (((phdr.p_filesz + pre_padding) % 4) != 0)
214                                 post_padding = 4 - ((phdr.p_filesz + pre_padding) % 4);
215
216                         ram_offset = (phdr.p_vaddr & ~r->start_address) - pre_padding;
217                         block_size = phdr.p_filesz + pre_padding + post_padding;
218
219                         fprintf(stderr, "%s, id: %d, offset: 0x%08x\n", r->name, r->id, ram_offset);
220                         fprintf(stderr, "vaddr: 0x%08lx paddr: 0x%08lx\n", phdr.p_vaddr, phdr.p_paddr);
221                         fprintf(stderr, "alignment: %ld\n", phdr.p_align);
222                         fprintf(stderr, "block_size: %d\n", block_size);
223
224                         /* ram type */
225                         fwrite_le32(r->id, output_file);
226
227                         /* block size */
228                         fwrite_le32(block_size, output_file);
229
230                         /* ram offset */
231                         fwrite_le32(ram_offset, output_file);
232
233                         /* reserved */
234                         fwrite_le32(0, output_file);
235
236                         /* pre padding */
237                         for (i = 0; i < pre_padding; i++)
238                                 fwrite(&zero, sizeof(zero), 1, output_file);
239
240                         /* data */
241                         fwrite(data + phdr.p_offset, phdr.p_filesz, 1, output_file);
242
243                         /* post padding */
244                         for (i = 0; i < post_padding; i++)
245                                 fwrite(&zero, sizeof(zero), 1, output_file);
246                 }
247         }
248
249         return 0;
250 }
251
252 int main(int argc, char *argv[])
253 {
254         int fd;
255         Elf *elf;
256         unsigned int version;
257         Elf_Kind kind;
258         FILE *output_file;
259         int ret;
260
261         if (argc != 3) {
262                 fprintf(stderr, "usage: %s <elf_file> <output_file>\n", argv[0]);
263                 exit(EXIT_FAILURE);
264         }
265
266         version = elf_version(EV_CURRENT);
267         if (version == EV_NONE) {
268                 fprintf(stderr, "Cannot initialize ELF library: %s\n",
269                         elf_errmsg(elf_errno()));
270                 exit(EXIT_FAILURE);
271         }
272
273         fd = open(argv[1], O_RDONLY);
274         if (fd < 0) {
275                 fprintf(stderr, "Cannot open \"%s\": %s", argv[1],
276                         strerror(errno));
277                 exit(EXIT_FAILURE);
278         }
279
280         elf = elf_begin(fd, ELF_C_READ, NULL);
281         if (elf == NULL) {
282                 fprintf(stderr, "Cannot initialize ELF descriptor: %s.",
283                         elf_errmsg(elf_errno()));
284                 exit(EXIT_FAILURE);
285         }
286
287         kind = elf_kind(elf);
288         if (kind != ELF_K_ELF) {
289                 fprintf(stderr, "\"%s\" is not an ELF file.\n", argv[1]);
290                 exit(EXIT_FAILURE);
291         }
292
293         output_file = fopen(argv[2], "wb");
294         if (output_file == NULL) {
295                 fprintf(stderr, "Cannot open output file \"%s\": %s", argv[1],
296                         strerror(errno));
297                 exit(EXIT_FAILURE);
298         }
299
300         ret = convert_elf_to_sst_firmware(elf, output_file);
301         if (ret < 0) {
302                 fprintf(stderr, "Cannot convert elf to SST firmware file\n");
303                 exit(EXIT_FAILURE);
304         }
305
306         elf_end(elf);
307         close(fd);
308
309         exit(EXIT_SUCCESS);
310 }