diff options
Diffstat (limited to 'utils/imxtools/elftosb.c')
-rw-r--r-- | utils/imxtools/elftosb.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/utils/imxtools/elftosb.c b/utils/imxtools/elftosb.c new file mode 100644 index 0000000000..2f8700551f --- /dev/null +++ b/utils/imxtools/elftosb.c | |||
@@ -0,0 +1,461 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #define _ISOC99_SOURCE | ||
23 | #define _POSIX_C_SOURCE 200809L /* for strdup */ | ||
24 | #include <stdio.h> | ||
25 | #include <errno.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <string.h> | ||
28 | #include <ctype.h> | ||
29 | #include <time.h> | ||
30 | #include <stdarg.h> | ||
31 | #include <strings.h> | ||
32 | #include <getopt.h> | ||
33 | |||
34 | #include "crypto.h" | ||
35 | #include "elf.h" | ||
36 | #include "sb.h" | ||
37 | #include "dbparser.h" | ||
38 | #include "misc.h" | ||
39 | #include "sb.h" | ||
40 | |||
41 | char **g_extern; | ||
42 | int g_extern_count; | ||
43 | |||
44 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) | ||
45 | |||
46 | #define crypto_cbc(...) \ | ||
47 | do { int ret = crypto_cbc(__VA_ARGS__); \ | ||
48 | if(ret != CRYPTO_ERROR_SUCCESS) \ | ||
49 | bug("crypto_cbc error: %d\n", ret); \ | ||
50 | }while(0) | ||
51 | |||
52 | /** | ||
53 | * command file to sb conversion | ||
54 | */ | ||
55 | |||
56 | static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) | ||
57 | { | ||
58 | if(fseek((FILE *)user, addr, SEEK_SET) == -1) | ||
59 | return false; | ||
60 | return fread(buf, 1, count, (FILE *)user) == count; | ||
61 | } | ||
62 | |||
63 | static void elf_printf(void *user, bool error, const char *fmt, ...) | ||
64 | { | ||
65 | if(!g_debug && !error) | ||
66 | return; | ||
67 | (void) user; | ||
68 | va_list args; | ||
69 | va_start(args, fmt); | ||
70 | vprintf(fmt, args); | ||
71 | va_end(args); | ||
72 | } | ||
73 | |||
74 | static void resolve_extern(struct cmd_source_t *src) | ||
75 | { | ||
76 | if(!src->is_extern) | ||
77 | return; | ||
78 | src->is_extern = false; | ||
79 | if(src->extern_nr < 0 || src->extern_nr >= g_extern_count) | ||
80 | bug("There aren't enough file on command file to resolve extern(%d)\n", src->extern_nr); | ||
81 | /* first free the old src->filename content */ | ||
82 | free(src->filename); | ||
83 | src->filename = strdup(g_extern[src->extern_nr]); | ||
84 | } | ||
85 | |||
86 | static void load_elf_by_id(struct cmd_file_t *cmd_file, const char *id) | ||
87 | { | ||
88 | struct cmd_source_t *src = db_find_source_by_id(cmd_file, id); | ||
89 | if(src == NULL) | ||
90 | bug("undefined reference to source '%s'\n", id); | ||
91 | /* avoid reloading */ | ||
92 | if(src->type == CMD_SRC_ELF && src->loaded) | ||
93 | return; | ||
94 | if(src->type != CMD_SRC_UNK) | ||
95 | bug("source '%s' seen both as elf and binary file\n", id); | ||
96 | /* resolve potential extern file */ | ||
97 | resolve_extern(src); | ||
98 | /* load it */ | ||
99 | src->type = CMD_SRC_ELF; | ||
100 | FILE *fd = fopen(src->filename, "rb"); | ||
101 | if(fd == NULL) | ||
102 | bug("cannot open '%s' (id '%s')\n", src->filename, id); | ||
103 | if(g_debug) | ||
104 | printf("Loading ELF file '%s'...\n", src->filename); | ||
105 | elf_init(&src->elf); | ||
106 | src->loaded = elf_read_file(&src->elf, elf_read, elf_printf, fd); | ||
107 | fclose(fd); | ||
108 | if(!src->loaded) | ||
109 | bug("error loading elf file '%s' (id '%s')\n", src->filename, id); | ||
110 | elf_translate_addresses(&src->elf); | ||
111 | } | ||
112 | |||
113 | static void load_bin_by_id(struct cmd_file_t *cmd_file, const char *id) | ||
114 | { | ||
115 | struct cmd_source_t *src = db_find_source_by_id(cmd_file, id); | ||
116 | if(src == NULL) | ||
117 | bug("undefined reference to source '%s'\n", id); | ||
118 | /* avoid reloading */ | ||
119 | if(src->type == CMD_SRC_BIN && src->loaded) | ||
120 | return; | ||
121 | if(src->type != CMD_SRC_UNK) | ||
122 | bug("source '%s' seen both as elf and binary file\n", id); | ||
123 | /* resolve potential extern file */ | ||
124 | resolve_extern(src); | ||
125 | /* load it */ | ||
126 | src->type = CMD_SRC_BIN; | ||
127 | FILE *fd = fopen(src->filename, "rb"); | ||
128 | if(fd == NULL) | ||
129 | bug("cannot open '%s' (id '%s')\n", src->filename, id); | ||
130 | if(g_debug) | ||
131 | printf("Loading BIN file '%s'...\n", src->filename); | ||
132 | fseek(fd, 0, SEEK_END); | ||
133 | src->bin.size = ftell(fd); | ||
134 | fseek(fd, 0, SEEK_SET); | ||
135 | src->bin.data = xmalloc(src->bin.size); | ||
136 | fread(src->bin.data, 1, src->bin.size, fd); | ||
137 | fclose(fd); | ||
138 | src->loaded = true; | ||
139 | } | ||
140 | |||
141 | static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) | ||
142 | { | ||
143 | struct sb_file_t *sb = xmalloc(sizeof(struct sb_file_t)); | ||
144 | memset(sb, 0, sizeof(struct sb_file_t)); | ||
145 | |||
146 | db_generate_default_sb_version(&sb->product_ver); | ||
147 | db_generate_default_sb_version(&sb->component_ver); | ||
148 | |||
149 | if(g_debug) | ||
150 | printf("Applying command file...\n"); | ||
151 | /* count sections */ | ||
152 | struct cmd_section_t *csec = cmd_file->section_list; | ||
153 | while(csec) | ||
154 | { | ||
155 | sb->nr_sections++; | ||
156 | csec = csec->next; | ||
157 | } | ||
158 | |||
159 | sb->sections = xmalloc(sb->nr_sections * sizeof(struct sb_section_t)); | ||
160 | memset(sb->sections, 0, sb->nr_sections * sizeof(struct sb_section_t)); | ||
161 | /* flatten sections */ | ||
162 | csec = cmd_file->section_list; | ||
163 | for(int i = 0; i < sb->nr_sections; i++, csec = csec->next) | ||
164 | { | ||
165 | struct sb_section_t *sec = &sb->sections[i]; | ||
166 | sec->identifier = csec->identifier; | ||
167 | |||
168 | /* options */ | ||
169 | do | ||
170 | { | ||
171 | /* cleartext */ | ||
172 | struct cmd_option_t *opt = db_find_option_by_id(csec->opt_list, "cleartext"); | ||
173 | if(opt != NULL) | ||
174 | { | ||
175 | if(opt->is_string) | ||
176 | bug("Cleartext section attribute must be an integer\n"); | ||
177 | if(opt->val != 0 && opt->val != 1) | ||
178 | bug("Cleartext section attribute must be 0 or 1\n"); | ||
179 | sec->is_cleartext = opt->val; | ||
180 | } | ||
181 | /* alignment */ | ||
182 | opt = db_find_option_by_id(csec->opt_list, "alignment"); | ||
183 | if(opt != NULL) | ||
184 | { | ||
185 | if(opt->is_string) | ||
186 | bug("Cleartext section attribute must be an integer\n"); | ||
187 | // n is a power of 2 iff n & (n - 1) = 0 | ||
188 | // alignement cannot be lower than block size | ||
189 | if((opt->val & (opt->val - 1)) != 0) | ||
190 | bug("Cleartext section attribute must be a power of two\n"); | ||
191 | if(opt->val < BLOCK_SIZE) | ||
192 | sec->alignment = BLOCK_SIZE; | ||
193 | else | ||
194 | sec->alignment = opt->val; | ||
195 | } | ||
196 | else | ||
197 | sec->alignment = BLOCK_SIZE; | ||
198 | }while(0); | ||
199 | |||
200 | if(csec->is_data) | ||
201 | { | ||
202 | sec->is_data = true; | ||
203 | sec->nr_insts = 1; | ||
204 | sec->insts = xmalloc(sec->nr_insts * sizeof(struct sb_inst_t)); | ||
205 | memset(sec->insts, 0, sec->nr_insts * sizeof(struct sb_inst_t)); | ||
206 | |||
207 | load_bin_by_id(cmd_file, csec->source_id); | ||
208 | struct bin_param_t *bin = &db_find_source_by_id(cmd_file, csec->source_id)->bin; | ||
209 | |||
210 | sec->insts[0].inst = SB_INST_DATA; | ||
211 | sec->insts[0].size = bin->size; | ||
212 | sec->insts[0].data = memdup(bin->data, bin->size); | ||
213 | } | ||
214 | else | ||
215 | { | ||
216 | sec->is_data = false; | ||
217 | /* count instructions and loads things */ | ||
218 | struct cmd_inst_t *cinst = csec->inst_list; | ||
219 | while(cinst) | ||
220 | { | ||
221 | if(cinst->type == CMD_LOAD) | ||
222 | { | ||
223 | load_elf_by_id(cmd_file, cinst->identifier); | ||
224 | struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; | ||
225 | sec->nr_insts += elf_get_nr_sections(elf); | ||
226 | } | ||
227 | else if(cinst->type == CMD_JUMP || cinst->type == CMD_CALL) | ||
228 | { | ||
229 | load_elf_by_id(cmd_file, cinst->identifier); | ||
230 | struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; | ||
231 | if(!elf_get_start_addr(elf, NULL)) | ||
232 | bug("cannot jump/call '%s' because it has no starting point !\n", cinst->identifier); | ||
233 | sec->nr_insts++; | ||
234 | } | ||
235 | else if(cinst->type == CMD_CALL_AT || cinst->type == CMD_JUMP_AT) | ||
236 | { | ||
237 | sec->nr_insts++; | ||
238 | } | ||
239 | else if(cinst->type == CMD_LOAD_AT) | ||
240 | { | ||
241 | load_bin_by_id(cmd_file, cinst->identifier); | ||
242 | sec->nr_insts++; | ||
243 | } | ||
244 | else if(cinst->type == CMD_MODE) | ||
245 | { | ||
246 | sec->nr_insts++; | ||
247 | } | ||
248 | else | ||
249 | bug("die\n"); | ||
250 | |||
251 | cinst = cinst->next; | ||
252 | } | ||
253 | |||
254 | sec->insts = xmalloc(sec->nr_insts * sizeof(struct sb_inst_t)); | ||
255 | memset(sec->insts, 0, sec->nr_insts * sizeof(struct sb_inst_t)); | ||
256 | /* flatten */ | ||
257 | int idx = 0; | ||
258 | cinst = csec->inst_list; | ||
259 | while(cinst) | ||
260 | { | ||
261 | if(cinst->type == CMD_LOAD) | ||
262 | { | ||
263 | struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; | ||
264 | struct elf_section_t *esec = elf->first_section; | ||
265 | while(esec) | ||
266 | { | ||
267 | if(esec->type == EST_LOAD) | ||
268 | { | ||
269 | sec->insts[idx].inst = SB_INST_LOAD; | ||
270 | sec->insts[idx].addr = esec->addr; | ||
271 | sec->insts[idx].size = esec->size; | ||
272 | sec->insts[idx++].data = memdup(esec->section, esec->size); | ||
273 | } | ||
274 | else if(esec->type == EST_FILL) | ||
275 | { | ||
276 | sec->insts[idx].inst = SB_INST_FILL; | ||
277 | sec->insts[idx].addr = esec->addr; | ||
278 | sec->insts[idx].size = esec->size; | ||
279 | sec->insts[idx++].pattern = esec->pattern; | ||
280 | } | ||
281 | esec = esec->next; | ||
282 | } | ||
283 | } | ||
284 | else if(cinst->type == CMD_JUMP || cinst->type == CMD_CALL) | ||
285 | { | ||
286 | struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; | ||
287 | sec->insts[idx].argument = cinst->argument; | ||
288 | sec->insts[idx].inst = (cinst->type == CMD_JUMP) ? SB_INST_JUMP : SB_INST_CALL; | ||
289 | sec->insts[idx++].addr = elf->start_addr; | ||
290 | } | ||
291 | else if(cinst->type == CMD_JUMP_AT || cinst->type == CMD_CALL_AT) | ||
292 | { | ||
293 | sec->insts[idx].argument = cinst->argument; | ||
294 | sec->insts[idx].inst = (cinst->type == CMD_JUMP_AT) ? SB_INST_JUMP : SB_INST_CALL; | ||
295 | sec->insts[idx++].addr = cinst->addr; | ||
296 | } | ||
297 | else if(cinst->type == CMD_LOAD_AT) | ||
298 | { | ||
299 | struct bin_param_t *bin = &db_find_source_by_id(cmd_file, cinst->identifier)->bin; | ||
300 | sec->insts[idx].inst = SB_INST_LOAD; | ||
301 | sec->insts[idx].addr = cinst->addr; | ||
302 | sec->insts[idx].data = memdup(bin->data, bin->size); | ||
303 | sec->insts[idx++].size = bin->size; | ||
304 | } | ||
305 | else if(cinst->type == CMD_MODE) | ||
306 | { | ||
307 | sec->insts[idx].inst = SB_INST_MODE; | ||
308 | sec->insts[idx++].addr = cinst->argument; | ||
309 | } | ||
310 | else | ||
311 | bug("die\n"); | ||
312 | |||
313 | cinst = cinst->next; | ||
314 | } | ||
315 | } | ||
316 | } | ||
317 | |||
318 | return sb; | ||
319 | } | ||
320 | |||
321 | void usage(void) | ||
322 | { | ||
323 | printf("Usage: elftosb [options | file]...\n"); | ||
324 | printf("Options:\n"); | ||
325 | printf(" -?/--help\tDisplay this message\n"); | ||
326 | printf(" -o <file>\tSet output file\n"); | ||
327 | printf(" -c <file>\tSet command file\n"); | ||
328 | printf(" -d/--debug\tEnable debug output\n"); | ||
329 | printf(" -k <file>\tAdd key file\n"); | ||
330 | printf(" -z\t\tAdd zero key\n"); | ||
331 | printf(" --add-key <key>\tAdd single key (hex or usbotp)\n"); | ||
332 | printf(" --real-key <key>\tOverride real key\n"); | ||
333 | printf(" --crypto-iv <iv>\tOverride crypto IV\n"); | ||
334 | exit(1); | ||
335 | } | ||
336 | |||
337 | static struct crypto_key_t g_zero_key = | ||
338 | { | ||
339 | .method = CRYPTO_KEY, | ||
340 | .u.key = {0} | ||
341 | }; | ||
342 | |||
343 | int main(int argc, char **argv) | ||
344 | { | ||
345 | char *cmd_filename = NULL; | ||
346 | char *output_filename = NULL; | ||
347 | struct crypto_key_t real_key; | ||
348 | struct crypto_key_t crypto_iv; | ||
349 | real_key.method = CRYPTO_NONE; | ||
350 | crypto_iv.method = CRYPTO_NONE; | ||
351 | |||
352 | while(1) | ||
353 | { | ||
354 | static struct option long_options[] = | ||
355 | { | ||
356 | {"help", no_argument, 0, '?'}, | ||
357 | {"debug", no_argument, 0, 'd'}, | ||
358 | {"add-key", required_argument, 0, 'a'}, | ||
359 | {"real-key", required_argument, 0, 'r'}, | ||
360 | {"crypto-iv", required_argument, 0, 'i'}, | ||
361 | {0, 0, 0, 0} | ||
362 | }; | ||
363 | |||
364 | int c = getopt_long(argc, argv, "?do:c:k:za:", long_options, NULL); | ||
365 | if(c == -1) | ||
366 | break; | ||
367 | switch(c) | ||
368 | { | ||
369 | case 'd': | ||
370 | g_debug = true; | ||
371 | break; | ||
372 | case '?': | ||
373 | usage(); | ||
374 | break; | ||
375 | case 'o': | ||
376 | output_filename = optarg; | ||
377 | break; | ||
378 | case 'c': | ||
379 | cmd_filename = optarg; | ||
380 | break; | ||
381 | case 'k': | ||
382 | { | ||
383 | add_keys_from_file(optarg); | ||
384 | break; | ||
385 | } | ||
386 | case 'z': | ||
387 | { | ||
388 | add_keys(&g_zero_key, 1); | ||
389 | break; | ||
390 | } | ||
391 | case 'a': | ||
392 | case 'r': | ||
393 | case 'i': | ||
394 | { | ||
395 | struct crypto_key_t key; | ||
396 | char *s = optarg; | ||
397 | if(!parse_key(&s, &key)) | ||
398 | bug("Invalid key/iv specified as argument"); | ||
399 | if(*s != 0) | ||
400 | bug("Trailing characters after key/iv specified as argument"); | ||
401 | if(c == 'r') | ||
402 | memcpy(&real_key, &key, sizeof(key)); | ||
403 | else if(c == 'i') | ||
404 | memcpy(&crypto_iv, &key, sizeof(key)); | ||
405 | else | ||
406 | add_keys(&key, 1); | ||
407 | break; | ||
408 | } | ||
409 | default: | ||
410 | abort(); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | if(!cmd_filename) | ||
415 | bug("You must specify a command file\n"); | ||
416 | if(!output_filename) | ||
417 | bug("You must specify an output file\n"); | ||
418 | |||
419 | g_extern = &argv[optind]; | ||
420 | g_extern_count = argc - optind; | ||
421 | |||
422 | if(g_debug) | ||
423 | { | ||
424 | printf("key: %d\n", g_nr_keys); | ||
425 | for(int i = 0; i < g_nr_keys; i++) | ||
426 | { | ||
427 | printf(" "); | ||
428 | print_key(&g_key_array[i], true); | ||
429 | } | ||
430 | |||
431 | for(int i = 0; i < g_extern_count; i++) | ||
432 | printf("extern(%d)=%s\n", i, g_extern[i]); | ||
433 | } | ||
434 | |||
435 | struct cmd_file_t *cmd_file = db_parse_file(cmd_filename); | ||
436 | struct sb_file_t *sb_file = apply_cmd_file(cmd_file); | ||
437 | db_free(cmd_file); | ||
438 | |||
439 | if(real_key.method == CRYPTO_KEY) | ||
440 | { | ||
441 | sb_file->override_real_key = true; | ||
442 | memcpy(sb_file->real_key, real_key.u.key, 16); | ||
443 | } | ||
444 | if(crypto_iv.method == CRYPTO_KEY) | ||
445 | { | ||
446 | sb_file->override_crypto_iv = true; | ||
447 | memcpy(sb_file->crypto_iv, crypto_iv.u.key, 16); | ||
448 | } | ||
449 | |||
450 | /* fill with default parameters since there is no command file support for them */ | ||
451 | sb_file->drive_tag = 0; | ||
452 | sb_file->first_boot_sec_id = sb_file->sections[0].identifier; | ||
453 | sb_file->flags = 0; | ||
454 | sb_file->minor_version = 1; | ||
455 | |||
456 | sb_write_file(sb_file, output_filename); | ||
457 | sb_free(sb_file); | ||
458 | clear_keys(); | ||
459 | |||
460 | return 0; | ||
461 | } | ||