diff options
Diffstat (limited to 'utils/mkimxboot/mkimxboot.c')
-rw-r--r-- | utils/mkimxboot/mkimxboot.c | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/utils/mkimxboot/mkimxboot.c b/utils/mkimxboot/mkimxboot.c new file mode 100644 index 0000000000..0483b5aeee --- /dev/null +++ b/utils/mkimxboot/mkimxboot.c | |||
@@ -0,0 +1,1123 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by 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 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <stdarg.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include "mkimxboot.h" | ||
27 | #include "sb.h" | ||
28 | #include "dualboot.h" | ||
29 | #include "md5.h" | ||
30 | #include "elf.h" | ||
31 | |||
32 | /* abstract structure to represent a Rockbox firmware. It can be a scrambled file | ||
33 | * or an ELF file or whatever. */ | ||
34 | struct rb_fw_t | ||
35 | { | ||
36 | int nr_insts; | ||
37 | struct sb_inst_t *insts; | ||
38 | int entry_idx; | ||
39 | }; | ||
40 | |||
41 | /* A firmware upgrade can contains several variants like recovery image, or | ||
42 | * images for different models */ | ||
43 | struct imx_fw_variant_desc_t | ||
44 | { | ||
45 | /* Offset within file */ | ||
46 | size_t offset; | ||
47 | /* Total size of the firmware */ | ||
48 | size_t size; | ||
49 | }; | ||
50 | |||
51 | /* Map a MD5 sum of the whole file to a model and describe the variants in it */ | ||
52 | struct imx_md5sum_t | ||
53 | { | ||
54 | /* Device model */ | ||
55 | enum imx_model_t model; | ||
56 | /* md5sum of the file */ | ||
57 | char *md5sum; | ||
58 | /* Version string */ | ||
59 | const char *version; | ||
60 | /* Variant descriptions */ | ||
61 | struct imx_fw_variant_desc_t fw_variants[VARIANT_COUNT]; | ||
62 | }; | ||
63 | |||
64 | /* Describe how to produce a bootloader image for a specific model */ | ||
65 | struct imx_model_desc_t | ||
66 | { | ||
67 | /* Descriptive name of this model */ | ||
68 | const char *model_name; | ||
69 | /* Dualboot code for this model */ | ||
70 | const unsigned char *dualboot; | ||
71 | /* Size of dualboot functions for this model */ | ||
72 | int dualboot_size; | ||
73 | /* Model name used in the Rockbox header in ".sansa" files - these match the | ||
74 | -add parameter to the "scramble" tool */ | ||
75 | const char *rb_model_name; | ||
76 | /* Model number used to initialise the checksum in the Rockbox header in | ||
77 | ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */ | ||
78 | const int rb_model_num; | ||
79 | /* Array of NULL-terminated keys */ | ||
80 | struct crypto_key_t **keys; | ||
81 | /* Dualboot load address */ | ||
82 | uint32_t dualboot_addr; | ||
83 | /* Bootloader load address */ | ||
84 | uint32_t bootloader_addr; | ||
85 | }; | ||
86 | |||
87 | /* Friendly names for variants */ | ||
88 | static const char *imx_fw_variant[] = | ||
89 | { | ||
90 | [VARIANT_DEFAULT] = "default", | ||
91 | [VARIANT_ZENXFI2_RECOVERY] = "ZEN X-Fi2 Recovery", | ||
92 | [VARIANT_ZENXFI2_NAND] = "ZEN X-Fi2 NAND", | ||
93 | [VARIANT_ZENXFI2_SD] = "ZEN X-Fi2 eMMC/SD", | ||
94 | [VARIANT_ZENXFISTYLE_RECOVERY] = "ZEN X-Fi Style Recovery", | ||
95 | [VARIANT_ZENSTYLE_RECOVERY] = "ZEN Style 100/300 Recovery", | ||
96 | }; | ||
97 | |||
98 | /* List of known MD5 sums for firmware upgrades */ | ||
99 | static const struct imx_md5sum_t imx_sums[] = | ||
100 | { | ||
101 | /** Fuze+ */ | ||
102 | { | ||
103 | /* Version 2.38.6 */ | ||
104 | MODEL_FUZEPLUS, "c3e27620a877dc6b200b97dcb3e0ecc7", "2.38.6", | ||
105 | { [VARIANT_DEFAULT] = { 0, 34652624 } } | ||
106 | }, | ||
107 | /** Zen X-Fi2 */ | ||
108 | { | ||
109 | /* Version 1.23.01 */ | ||
110 | MODEL_ZENXFI2, "e37e2c24abdff8e624d0a29f79157850", "1.23.01", | ||
111 | { | ||
112 | [VARIANT_ZENXFI2_RECOVERY] = { 602128, 684192}, | ||
113 | [VARIANT_ZENXFI2_NAND] = { 1286320, 42406608 }, | ||
114 | [VARIANT_ZENXFI2_SD] = { 43692928, 42304208 } | ||
115 | } | ||
116 | }, | ||
117 | { | ||
118 | /* Version 1.23.01e */ | ||
119 | MODEL_ZENXFI2, "2beff2168212d332f13cfc36ca46989d", "1.23.01e", | ||
120 | { | ||
121 | [VARIANT_ZENXFI2_RECOVERY] = { 0x93010, 684192}, | ||
122 | [VARIANT_ZENXFI2_NAND] = { 0x13a0b0, 42410704 }, | ||
123 | [VARIANT_ZENXFI2_SD] = { 0x29ac380, 42304208 } | ||
124 | } | ||
125 | }, | ||
126 | /** Zen X-Fi3 */ | ||
127 | { | ||
128 | /* Version 1.00.15e */ | ||
129 | MODEL_ZENXFI3, "658a24eeef5f7186ca731085d8822a87", "1.00.15e", | ||
130 | { [VARIANT_DEFAULT] = {0, 18110576} } | ||
131 | }, | ||
132 | { | ||
133 | /* Version 1.00.22e */ | ||
134 | MODEL_ZENXFI3, "a5114cd45ea4554ec221f51a71083862", "1.00.22e", | ||
135 | { [VARIANT_DEFAULT] = {0, 18110576} } | ||
136 | }, | ||
137 | { | ||
138 | /* Version 1.00.25 */ | ||
139 | MODEL_ZENXFI3, "a41a3a78f86a4ac2879d194c6d528059", "1.00.25", | ||
140 | { [VARIANT_DEFAULT] = {0, 18110576 } } | ||
141 | }, | ||
142 | { | ||
143 | /* Version 1.00.25e */ | ||
144 | MODEL_ZENXFI3, "c180f57e2b2d62620f87a1d853f349ff", "1.00.25e", | ||
145 | { [VARIANT_DEFAULT] = {0, 18110576 } } | ||
146 | }, | ||
147 | /** Zen X-Fi Style */ | ||
148 | { | ||
149 | /* Version 1.03.04e */ | ||
150 | MODEL_ZENXFISTYLE, "32a731b7f714e9f99a95991003759c98", "1.03.04", | ||
151 | { | ||
152 | [VARIANT_DEFAULT] = {842960, 29876944}, | ||
153 | [VARIANT_ZENXFISTYLE_RECOVERY] = {610272, 232688}, | ||
154 | } | ||
155 | }, | ||
156 | { | ||
157 | /* Version 1.03.04e */ | ||
158 | MODEL_ZENXFISTYLE, "2c7ee52d9984d85dd39aa49b3331e66c", "1.03.04e", | ||
159 | { | ||
160 | [VARIANT_DEFAULT] = {842960, 29876944}, | ||
161 | [VARIANT_ZENXFISTYLE_RECOVERY] = {610272, 232688}, | ||
162 | } | ||
163 | }, | ||
164 | { | ||
165 | /* Version 1.03.04e */ | ||
166 | MODEL_ZENSTYLE, "dbebec8fe666412061d9740ff68605dd", "1.03.04e", | ||
167 | { | ||
168 | [VARIANT_DEFAULT] = {758848, 6641344}, | ||
169 | [VARIANT_ZENSTYLE_RECOVERY] = {610272, 148576}, | ||
170 | } | ||
171 | }, | ||
172 | /** Sony NWZ-E370 */ | ||
173 | { | ||
174 | /* Version 1.00.00 */ | ||
175 | MODEL_NWZE370, "a615fdb70b3e1bfb0355a5bc2bf237ab", "1.00.00", | ||
176 | { [VARIANT_DEFAULT] = {0, 16056320 } } | ||
177 | }, | ||
178 | { | ||
179 | /* Version 1.00.01 */ | ||
180 | MODEL_NWZE370, "ee83f3c6026cbcc07097867f06fd585f", "1.00.01", | ||
181 | { [VARIANT_DEFAULT] = {0, 16515072 } } | ||
182 | }, | ||
183 | /** Sony NWZ-E360 */ | ||
184 | { | ||
185 | /* Version 1.00.00 */ | ||
186 | MODEL_NWZE360, "d0047f8a87d456a0032297b3c802a1ff", "1.00.00", | ||
187 | { [VARIANT_DEFAULT] = {0, 20652032 } } | ||
188 | }, | ||
189 | /** Sony NWZ-E380 */ | ||
190 | { | ||
191 | /* Version 1.00.00 */ | ||
192 | MODEL_NWZE370, "412f8ccd453195c0bebcc1fd8376322f", "1.00.00", | ||
193 | { [VARIANT_DEFAULT] = {0, 16429056 } } | ||
194 | }, | ||
195 | { | ||
196 | /* Version 1.00.200 */ | ||
197 | MODEL_NWZE370, "75cfa51078261c547717e11a4676f1af", "1.00.200", | ||
198 | { [VARIANT_DEFAULT] = {0, 16429056 } } | ||
199 | } | ||
200 | }; | ||
201 | |||
202 | static struct crypto_key_t zero_key = | ||
203 | { | ||
204 | .method = CRYPTO_KEY, | ||
205 | .u.key = {0} | ||
206 | }; | ||
207 | |||
208 | static struct crypto_key_t *list_zero_key[] = { &zero_key, NULL }; | ||
209 | static struct crypto_key_t *list_all_keys[] = { &zero_key, NULL }; | ||
210 | |||
211 | static const struct imx_model_desc_t imx_models[] = | ||
212 | { | ||
213 | [MODEL_FUZEPLUS] = {"Fuze+", dualboot_fuzeplus, sizeof(dualboot_fuzeplus), | ||
214 | "fuz+", 72, list_zero_key, 0, 0x40000000 }, | ||
215 | [MODEL_ZENXFI2] = {"Zen X-Fi2", dualboot_zenxfi2, sizeof(dualboot_zenxfi2), | ||
216 | "zxf2", 82, list_zero_key, 0, 0x40000000 }, | ||
217 | [MODEL_ZENXFI3] = {"Zen X-Fi3", dualboot_zenxfi3, sizeof(dualboot_zenxfi3), | ||
218 | "zxf3", 83, list_zero_key, 0, 0x40000000 }, | ||
219 | [MODEL_ZENXFISTYLE] = {"Zen X-Fi Style", dualboot_zenxfistyle, sizeof(dualboot_zenxfistyle), | ||
220 | "zxfs", 94, list_zero_key, 0, 0x40000000 }, | ||
221 | [MODEL_ZENSTYLE] = {"Zen Style 100/300", NULL, 0, "", -1, list_zero_key, 0, 0x40000000 }, | ||
222 | [MODEL_NWZE370] = {"NWZ-E370", dualboot_nwze370, sizeof(dualboot_nwze370), | ||
223 | "e370", 88, list_zero_key, 0, 0x40000000 }, | ||
224 | [MODEL_NWZE360] = {"NWZ-E360", dualboot_nwze360, sizeof(dualboot_nwze360), | ||
225 | "e360", 89, list_zero_key, 0, 0x40000000 }, | ||
226 | }; | ||
227 | |||
228 | #define NR_IMX_SUMS (sizeof(imx_sums) / sizeof(imx_sums[0])) | ||
229 | #define NR_IMX_MODELS (sizeof(imx_models) / sizeof(imx_models[0])) | ||
230 | |||
231 | #define MAGIC_ROCK 0x726f636b /* 'rock' */ | ||
232 | #define MAGIC_RECOVERY 0xfee1dead | ||
233 | #define MAGIC_NORMAL 0xcafebabe | ||
234 | #define MAGIC_CHARGE 0x67726863 /* 'chrg' */ | ||
235 | |||
236 | const char *imx_error_to_string(enum imx_error_t err) | ||
237 | { | ||
238 | switch(err) | ||
239 | { | ||
240 | case IMX_SUCCESS: return "success"; | ||
241 | case IMX_ERROR: return "error"; | ||
242 | case IMX_OPEN_ERROR: return "open error"; | ||
243 | case IMX_READ_ERROR: return "read error"; | ||
244 | case IMX_NO_MATCH: return "no match"; | ||
245 | case IMX_BOOT_INVALID: return "invalid"; | ||
246 | case IMX_BOOT_MISMATCH: return "mismatch"; | ||
247 | case IMX_BOOT_CHECKSUM_ERROR: return "checksum error"; | ||
248 | case IMX_DONT_KNOW_HOW_TO_PATCH: return "don't know how to patch"; | ||
249 | case IMX_VARIANT_MISMATCH: return "variant mismatch"; | ||
250 | case IMX_WRITE_ERROR: return "write error"; | ||
251 | case IMX_FIRST_SB_ERROR: return "sb error"; | ||
252 | case IMX_MODEL_MISMATCH: return "model mismatch"; | ||
253 | default: return "unknown error"; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | static void add_key_list(struct crypto_key_t **list) | ||
258 | { | ||
259 | while(*list != NULL) | ||
260 | add_keys(*list++, 1); | ||
261 | } | ||
262 | |||
263 | static int rb_fw_get_sb_inst_count(struct rb_fw_t *fw) | ||
264 | { | ||
265 | return fw->nr_insts; | ||
266 | } | ||
267 | |||
268 | /* fill sb instruction for the firmware, fill fill rb_fw_get_sb_inst_count() instructions */ | ||
269 | static void rb_fw_fill_sb(struct rb_fw_t *fw, struct sb_inst_t *inst, | ||
270 | uint32_t entry_arg) | ||
271 | { | ||
272 | memcpy(inst, fw->insts, fw->nr_insts * sizeof(struct sb_inst_t)); | ||
273 | /* copy data if needed */ | ||
274 | for(int i = 0; i < fw->nr_insts; i++) | ||
275 | if(fw->insts[i].inst == SB_INST_LOAD) | ||
276 | fw->insts[i].data = memdup(fw->insts[i].data, fw->insts[i].size); | ||
277 | /* replace call argument of the entry point */ | ||
278 | inst[fw->entry_idx].argument = entry_arg; | ||
279 | } | ||
280 | |||
281 | static enum imx_error_t patch_std_zero_host_play(int jump_before, | ||
282 | struct imx_option_t opt, struct sb_file_t *sb_file, struct rb_fw_t boot_fw) | ||
283 | { | ||
284 | /* We assume the file has three boot sections: ____, host, play and one | ||
285 | * resource section rsrc. | ||
286 | * | ||
287 | * Dual Boot: | ||
288 | * ---------- | ||
289 | * We patch the file by inserting the dualboot code before the <jump_before>th | ||
290 | * call in the ____ section. We give it as argument the section name 'rock' | ||
291 | * and add a section called 'rock' after rsrc which contains the bootloader. | ||
292 | * | ||
293 | * Single Boot & Recovery: | ||
294 | * ----------------------- | ||
295 | * We patch the file by inserting the bootloader code after the <jump_before>th | ||
296 | * call in the ____ section and get rid of everything else. In recovery mode, | ||
297 | * we give 0xfee1dead as argument */ | ||
298 | |||
299 | /* used to manipulate entries */ | ||
300 | int nr_boot_inst = rb_fw_get_sb_inst_count(&boot_fw); | ||
301 | |||
302 | /* first locate the good instruction */ | ||
303 | struct sb_section_t *sec = &sb_file->sections[0]; | ||
304 | int jump_idx = 0; | ||
305 | while(jump_idx < sec->nr_insts && jump_before > 0) | ||
306 | if(sec->insts[jump_idx++].inst == SB_INST_CALL) | ||
307 | jump_before--; | ||
308 | if(jump_idx == sec->nr_insts) | ||
309 | { | ||
310 | printf("[ERR] Cannot locate call in section ____\n"); | ||
311 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
312 | } | ||
313 | |||
314 | if(opt.output == IMX_DUALBOOT) | ||
315 | { | ||
316 | /* create a new instruction array with a hole for two instructions */ | ||
317 | struct sb_inst_t *new_insts = xmalloc(sizeof(struct sb_inst_t) * (sec->nr_insts + 2)); | ||
318 | memcpy(new_insts, sec->insts, sizeof(struct sb_inst_t) * jump_idx); | ||
319 | memcpy(new_insts + jump_idx + 2, sec->insts + jump_idx, | ||
320 | sizeof(struct sb_inst_t) * (sec->nr_insts - jump_idx)); | ||
321 | /* first instruction is be a load */ | ||
322 | struct sb_inst_t *load = &new_insts[jump_idx]; | ||
323 | memset(load, 0, sizeof(struct sb_inst_t)); | ||
324 | load->inst = SB_INST_LOAD; | ||
325 | load->size = imx_models[opt.model].dualboot_size; | ||
326 | load->addr = imx_models[opt.model].dualboot_addr; | ||
327 | /* duplicate memory because it will be free'd */ | ||
328 | load->data = memdup(imx_models[opt.model].dualboot, | ||
329 | imx_models[opt.model].dualboot_size); | ||
330 | /* second instruction is a call */ | ||
331 | struct sb_inst_t *call = &new_insts[jump_idx + 1]; | ||
332 | memset(call, 0, sizeof(struct sb_inst_t)); | ||
333 | call->inst = SB_INST_CALL; | ||
334 | call->addr = imx_models[opt.model].dualboot_addr; | ||
335 | call->argument = MAGIC_ROCK; | ||
336 | /* free old instruction array */ | ||
337 | free(sec->insts); | ||
338 | sec->insts = new_insts; | ||
339 | sec->nr_insts += 2; | ||
340 | |||
341 | /* create a new section */ | ||
342 | struct sb_section_t rock_sec; | ||
343 | memset(&rock_sec, 0, sizeof(rock_sec)); | ||
344 | /* section can have any number of instructions */ | ||
345 | rock_sec.identifier = MAGIC_ROCK; | ||
346 | rock_sec.alignment = BLOCK_SIZE; | ||
347 | rock_sec.nr_insts = nr_boot_inst; | ||
348 | rock_sec.insts = xmalloc(nr_boot_inst * sizeof(struct sb_inst_t)); | ||
349 | rb_fw_fill_sb(&boot_fw, rock_sec.insts, MAGIC_NORMAL); | ||
350 | |||
351 | sb_file->sections = augment_array(sb_file->sections, | ||
352 | sizeof(struct sb_section_t), sb_file->nr_sections, | ||
353 | &rock_sec, 1); | ||
354 | sb_file->nr_sections++; | ||
355 | |||
356 | return IMX_SUCCESS; | ||
357 | } | ||
358 | else if(opt.output == IMX_SINGLEBOOT || opt.output == IMX_RECOVERY) | ||
359 | { | ||
360 | bool recovery = (opt.output == IMX_RECOVERY); | ||
361 | /* remove everything after the call and add instructions for firmware */ | ||
362 | struct sb_inst_t *new_insts = xmalloc(sizeof(struct sb_inst_t) * (jump_idx + nr_boot_inst)); | ||
363 | memcpy(new_insts, sec->insts, sizeof(struct sb_inst_t) * jump_idx); | ||
364 | for(int i = jump_idx; i < sec->nr_insts; i++) | ||
365 | sb_free_instruction(sec->insts[i]); | ||
366 | rb_fw_fill_sb(&boot_fw, &new_insts[jump_idx], recovery ? MAGIC_RECOVERY : MAGIC_NORMAL); | ||
367 | |||
368 | free(sec->insts); | ||
369 | sec->insts = new_insts; | ||
370 | sec->nr_insts = jump_idx + nr_boot_inst; | ||
371 | /* remove all other sections */ | ||
372 | for(int i = 1; i < sb_file->nr_sections; i++) | ||
373 | sb_free_section(sb_file->sections[i]); | ||
374 | struct sb_section_t *new_sec = xmalloc(sizeof(struct sb_section_t)); | ||
375 | memcpy(new_sec, &sb_file->sections[0], sizeof(struct sb_section_t)); | ||
376 | free(sb_file->sections); | ||
377 | sb_file->sections = new_sec; | ||
378 | sb_file->nr_sections = 1; | ||
379 | |||
380 | return IMX_SUCCESS; | ||
381 | } | ||
382 | else if(opt.output == IMX_CHARGE) | ||
383 | { | ||
384 | /* throw away everything except the dualboot stub with a special argument */ | ||
385 | struct sb_inst_t *new_insts = xmalloc(sizeof(struct sb_inst_t) * 2); | ||
386 | /* first instruction is be a load */ | ||
387 | struct sb_inst_t *load = &new_insts[0]; | ||
388 | memset(load, 0, sizeof(struct sb_inst_t)); | ||
389 | load->inst = SB_INST_LOAD; | ||
390 | load->size = imx_models[opt.model].dualboot_size; | ||
391 | load->addr = imx_models[opt.model].dualboot_addr; | ||
392 | /* duplicate memory because it will be free'd */ | ||
393 | load->data = memdup(imx_models[opt.model].dualboot, | ||
394 | imx_models[opt.model].dualboot_size); | ||
395 | /* second instruction is a call */ | ||
396 | struct sb_inst_t *call = &new_insts[1]; | ||
397 | memset(call, 0, sizeof(struct sb_inst_t)); | ||
398 | call->inst = SB_INST_CALL; | ||
399 | call->addr = imx_models[opt.model].dualboot_addr; | ||
400 | call->argument = MAGIC_CHARGE; | ||
401 | /* free old instruction array */ | ||
402 | free(sec->insts); | ||
403 | sec->insts = new_insts; | ||
404 | sec->nr_insts = 2; | ||
405 | /* remove all other sections */ | ||
406 | for(int i = 1; i < sb_file->nr_sections; i++) | ||
407 | sb_free_section(sb_file->sections[i]); | ||
408 | struct sb_section_t *new_sec = xmalloc(sizeof(struct sb_section_t)); | ||
409 | memcpy(new_sec, &sb_file->sections[0], sizeof(struct sb_section_t)); | ||
410 | free(sb_file->sections); | ||
411 | sb_file->sections = new_sec; | ||
412 | sb_file->nr_sections = 1; | ||
413 | |||
414 | return IMX_SUCCESS; | ||
415 | } | ||
416 | else | ||
417 | { | ||
418 | printf("[ERR] Bad output type !\n"); | ||
419 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | static enum imx_error_t parse_subversion(const char *s, const char *end, uint16_t *ver) | ||
424 | { | ||
425 | int len = (end == NULL) ? strlen(s) : end - s; | ||
426 | if(len > 4) | ||
427 | { | ||
428 | printf("[ERR] Bad subversion override '%s' (too long)\n", s); | ||
429 | return IMX_ERROR; | ||
430 | } | ||
431 | *ver = 0; | ||
432 | for(int i = 0; i < len; i++) | ||
433 | { | ||
434 | if(!isdigit(s[i])) | ||
435 | { | ||
436 | printf("[ERR] Bad subversion override '%s' (not a digit)\n", s); | ||
437 | return IMX_ERROR; | ||
438 | } | ||
439 | *ver = *ver << 4 | (s[i] - '0'); | ||
440 | } | ||
441 | return IMX_SUCCESS; | ||
442 | } | ||
443 | |||
444 | static enum imx_error_t parse_version(const char *s, struct sb_version_t *ver) | ||
445 | { | ||
446 | const char *dot1 = strchr(s, '.'); | ||
447 | if(dot1 == NULL) | ||
448 | { | ||
449 | printf("[ERR] Bad version override '%s' (missing dot)\n", s); | ||
450 | return IMX_ERROR; | ||
451 | } | ||
452 | const char *dot2 = strchr(dot1 + 1, '.'); | ||
453 | if(dot2 == NULL) | ||
454 | { | ||
455 | printf("[ERR] Bad version override '%s' (missing second dot)\n", s); | ||
456 | return IMX_ERROR; | ||
457 | } | ||
458 | enum imx_error_t ret = parse_subversion(s, dot1, &ver->major); | ||
459 | if(ret != IMX_SUCCESS) return ret; | ||
460 | ret = parse_subversion(dot1 + 1, dot2, &ver->minor); | ||
461 | if(ret != IMX_SUCCESS) return ret; | ||
462 | ret = parse_subversion(dot2 + 1, NULL, &ver->revision); | ||
463 | if(ret != IMX_SUCCESS) return ret; | ||
464 | return IMX_SUCCESS; | ||
465 | } | ||
466 | |||
467 | static enum imx_error_t patch_firmware(struct imx_option_t opt, | ||
468 | struct sb_file_t *sb_file, struct rb_fw_t boot_fw) | ||
469 | { | ||
470 | if(opt.force_version) | ||
471 | { | ||
472 | enum imx_error_t err = parse_version(opt.force_version, &sb_file->product_ver); | ||
473 | if(err != IMX_SUCCESS) | ||
474 | return err; | ||
475 | err = parse_version(opt.force_version, &sb_file->component_ver); | ||
476 | if(err != IMX_SUCCESS) | ||
477 | return err; | ||
478 | } | ||
479 | switch(opt.model) | ||
480 | { | ||
481 | case MODEL_FUZEPLUS: | ||
482 | /* The Fuze+ uses the standard ____, host, play sections, patch after third | ||
483 | * call in ____ section */ | ||
484 | return patch_std_zero_host_play(3, opt, sb_file, boot_fw); | ||
485 | case MODEL_ZENXFI3: | ||
486 | /* The ZEN X-Fi3 uses the standard ____, hSst, pSay sections, patch after third | ||
487 | * call in ____ section. Although sections names use the S variant, they are standard. */ | ||
488 | return patch_std_zero_host_play(3, opt, sb_file, boot_fw); | ||
489 | case MODEL_NWZE360: | ||
490 | case MODEL_NWZE370: | ||
491 | /* The NWZ-E360/E370 uses the standard ____, host, play sections, patch after first | ||
492 | * call in ____ section. */ | ||
493 | return patch_std_zero_host_play(1, opt, sb_file, boot_fw); | ||
494 | case MODEL_ZENXFI2: | ||
495 | /* The ZEN X-Fi2 has two types of firmware: recovery and normal. | ||
496 | * Normal uses the standard ___, host, play sections and recovery only ____ */ | ||
497 | switch(opt.fw_variant) | ||
498 | { | ||
499 | case VARIANT_ZENXFI2_RECOVERY: | ||
500 | case VARIANT_ZENXFI2_NAND: | ||
501 | case VARIANT_ZENXFI2_SD: | ||
502 | return patch_std_zero_host_play(1, opt, sb_file, boot_fw); | ||
503 | default: | ||
504 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
505 | } | ||
506 | break; | ||
507 | case MODEL_ZENXFISTYLE: | ||
508 | /* The ZEN X-Fi Style uses the standard ____, host, play sections, patch after first | ||
509 | * call in ____ section. */ | ||
510 | return patch_std_zero_host_play(1, opt, sb_file, boot_fw); | ||
511 | default: | ||
512 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | static enum imx_error_t unpatch_std_zero_host_play(int jump_before, | ||
517 | struct imx_option_t opt, struct sb_file_t *sb_file) | ||
518 | { | ||
519 | /* find rockbox section */ | ||
520 | int rb_sec = -1; | ||
521 | for(int i = 0; i < sb_file->nr_sections; i++) | ||
522 | if(sb_file->sections[i].identifier == MAGIC_ROCK) | ||
523 | rb_sec = i; | ||
524 | if(rb_sec == -1) | ||
525 | { | ||
526 | printf("[ERR][INTERNAL] Cannot find rockbox section\n"); | ||
527 | return IMX_ERROR; | ||
528 | } | ||
529 | /** 1) remove rockbox section */ | ||
530 | /* free rockbox section */ | ||
531 | sb_free_section(sb_file->sections[rb_sec]); | ||
532 | /* create a new array of sections */ | ||
533 | sb_file->nr_sections--; | ||
534 | struct sb_section_t *new_sec = xmalloc(sb_file->nr_sections * sizeof(struct sb_section_t)); | ||
535 | /* copy all sections exception rockbox */ | ||
536 | memcpy(new_sec, sb_file->sections, rb_sec * sizeof(struct sb_section_t)); | ||
537 | memcpy(new_sec + rb_sec, sb_file->sections + rb_sec + 1, | ||
538 | (sb_file->nr_sections - rb_sec) * sizeof(struct sb_section_t)); | ||
539 | /* free old array and replace it */ | ||
540 | free(sb_file->sections); | ||
541 | sb_file->sections = new_sec; | ||
542 | |||
543 | /** 2) remove patch instructions in boot section */ | ||
544 | struct sb_section_t *sec = &sb_file->sections[0]; | ||
545 | int jump_idx = 0; | ||
546 | while(jump_idx < sec->nr_insts && jump_before > 0) | ||
547 | if(sec->insts[jump_idx++].inst == SB_INST_CALL) | ||
548 | jump_before--; | ||
549 | if(jump_idx == sec->nr_insts) | ||
550 | { | ||
551 | printf("[ERR] Cannot locate call in section ____\n"); | ||
552 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
553 | } | ||
554 | /* free two instructions */ | ||
555 | sb_free_instruction(sec->insts[jump_idx]); | ||
556 | sb_free_instruction(sec->insts[jump_idx + 1]); | ||
557 | /* create a new array of instructions */ | ||
558 | sec->nr_insts -= 2; | ||
559 | struct sb_inst_t *new_inst = xmalloc(sec->nr_insts * sizeof(struct sb_inst_t)); | ||
560 | /* copy all instructions except the two patch to remove */ | ||
561 | memcpy(new_inst, sec->insts, jump_idx * sizeof(struct sb_inst_t)); | ||
562 | memcpy(new_inst + jump_idx, sec->insts + jump_idx + 2, | ||
563 | (sec->nr_insts - jump_idx) * sizeof(struct sb_inst_t)); | ||
564 | /* free old array and replace it */ | ||
565 | free(sec->insts); | ||
566 | sec->insts = new_inst; | ||
567 | |||
568 | return IMX_SUCCESS; | ||
569 | } | ||
570 | |||
571 | static enum imx_error_t unpatch_firmware(struct imx_option_t opt, | ||
572 | struct sb_file_t *sb_file) | ||
573 | { | ||
574 | /* keep consistent with patch_firmware */ | ||
575 | switch(opt.model) | ||
576 | { | ||
577 | case MODEL_FUZEPLUS: | ||
578 | /* The Fuze+ uses the standard ____, host, play sections, patch after third | ||
579 | * call in ____ section */ | ||
580 | return unpatch_std_zero_host_play(3, opt, sb_file); | ||
581 | case MODEL_ZENXFI3: | ||
582 | /* The ZEN X-Fi3 uses the standard ____, hSst, pSay sections, patch after third | ||
583 | * call in ____ section. Although sections names use the S variant, they are standard. */ | ||
584 | return unpatch_std_zero_host_play(3, opt, sb_file); | ||
585 | case MODEL_NWZE360: | ||
586 | case MODEL_NWZE370: | ||
587 | /* The NWZ-E360/E370 uses the standard ____, host, play sections, patch after first | ||
588 | * call in ____ section. */ | ||
589 | return unpatch_std_zero_host_play(1, opt, sb_file); | ||
590 | case MODEL_ZENXFI2: | ||
591 | /* The ZEN X-Fi2 has two types of firmware: recovery and normal. | ||
592 | * Normal uses the standard ___, host, play sections and recovery only ____ */ | ||
593 | switch(opt.fw_variant) | ||
594 | { | ||
595 | case VARIANT_ZENXFI2_RECOVERY: | ||
596 | case VARIANT_ZENXFI2_NAND: | ||
597 | case VARIANT_ZENXFI2_SD: | ||
598 | return unpatch_std_zero_host_play(1, opt, sb_file); | ||
599 | default: | ||
600 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
601 | } | ||
602 | break; | ||
603 | case MODEL_ZENXFISTYLE: | ||
604 | /* The ZEN X-Fi Style uses the standard ____, host, play sections, patch after first | ||
605 | * call in ____ section. */ | ||
606 | return unpatch_std_zero_host_play(1, opt, sb_file); | ||
607 | default: | ||
608 | return IMX_DONT_KNOW_HOW_TO_PATCH; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | static uint32_t get_uint32be(unsigned char *p) | ||
613 | { | ||
614 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; | ||
615 | } | ||
616 | |||
617 | void dump_imx_dev_info(const char *prefix) | ||
618 | { | ||
619 | printf("%smkimxboot models:\n", prefix); | ||
620 | for(int i = 0; i < NR_IMX_MODELS; i++) | ||
621 | { | ||
622 | printf("%s %s: idx=%d rb_model=%s rb_num=%d\n", prefix, | ||
623 | imx_models[i].model_name, i, imx_models[i].rb_model_name, | ||
624 | imx_models[i].rb_model_num); | ||
625 | } | ||
626 | printf("%smkimxboot variants:\n", prefix); | ||
627 | for(int i = 0; i < VARIANT_COUNT; i++) | ||
628 | { | ||
629 | printf("%s %d: %s\n", prefix, i, imx_fw_variant[i]); | ||
630 | } | ||
631 | printf("%smkimxboot mapping:\n", prefix); | ||
632 | for(int i = 0; i < NR_IMX_SUMS; i++) | ||
633 | { | ||
634 | printf("%s md5sum=%s -> idx=%d, ver=%s\n", prefix, imx_sums[i].md5sum, | ||
635 | imx_sums[i].model, imx_sums[i].version); | ||
636 | for(int j = 0; j < VARIANT_COUNT; j++) | ||
637 | if(imx_sums[i].fw_variants[j].size) | ||
638 | printf("%s variant=%d -> offset=%#x size=%#x\n", prefix, | ||
639 | j, (unsigned)imx_sums[i].fw_variants[j].offset, | ||
640 | (unsigned)imx_sums[i].fw_variants[j].size); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | /* find an entry into imx_sums which matches the MD5 sum of a file */ | ||
645 | static enum imx_error_t find_model_by_md5sum(uint8_t file_md5sum[16], int *md5_idx) | ||
646 | { | ||
647 | int i = 0; | ||
648 | while(i < NR_IMX_SUMS) | ||
649 | { | ||
650 | uint8_t md5[20]; | ||
651 | if(strlen(imx_sums[i].md5sum) != 32) | ||
652 | { | ||
653 | printf("[INFO] Invalid MD5 sum in imx_sums\n"); | ||
654 | return IMX_ERROR; | ||
655 | } | ||
656 | for(int j = 0; j < 16; j++) | ||
657 | { | ||
658 | uint8_t a, b; | ||
659 | if(convxdigit(imx_sums[i].md5sum[2 * j], &a) || convxdigit(imx_sums[i].md5sum[2 * j + 1], &b)) | ||
660 | { | ||
661 | printf("[ERR][INTERNAL] Bad checksum format: %s\n", imx_sums[i].md5sum); | ||
662 | return IMX_ERROR; | ||
663 | } | ||
664 | md5[j] = (a << 4) | b; | ||
665 | } | ||
666 | if(memcmp(file_md5sum, md5, 16) == 0) | ||
667 | break; | ||
668 | i++; | ||
669 | } | ||
670 | if(i == NR_IMX_SUMS) | ||
671 | { | ||
672 | printf("[WARN] MD5 sum doesn't match any known file\n"); | ||
673 | return IMX_NO_MATCH; | ||
674 | } | ||
675 | *md5_idx = i; | ||
676 | return IMX_SUCCESS; | ||
677 | } | ||
678 | |||
679 | /* read a file to a buffer */ | ||
680 | static enum imx_error_t read_file(const char *file, void **buffer, size_t *size) | ||
681 | { | ||
682 | FILE *f = fopen(file, "rb"); | ||
683 | if(f == NULL) | ||
684 | { | ||
685 | printf("[ERR] Cannot open file '%s' for reading: %m\n", file); | ||
686 | return IMX_OPEN_ERROR; | ||
687 | } | ||
688 | fseek(f, 0, SEEK_END); | ||
689 | *size = ftell(f); | ||
690 | fseek(f, 0, SEEK_SET); | ||
691 | *buffer = xmalloc(*size); | ||
692 | if(fread(*buffer, *size, 1, f) != 1) | ||
693 | { | ||
694 | free(*buffer); | ||
695 | fclose(f); | ||
696 | printf("[ERR] Cannot read file '%s': %m\n", file); | ||
697 | return IMX_READ_ERROR; | ||
698 | } | ||
699 | fclose(f); | ||
700 | return IMX_SUCCESS; | ||
701 | } | ||
702 | |||
703 | /* write a file from a buffer */ | ||
704 | static enum imx_error_t write_file(const char *file, void *buffer, size_t size) | ||
705 | { | ||
706 | FILE *f = fopen(file, "wb"); | ||
707 | if(f == NULL) | ||
708 | { | ||
709 | printf("[ERR] Cannot open file '%s' for writing: %m\n", file); | ||
710 | return IMX_OPEN_ERROR; | ||
711 | } | ||
712 | if(fwrite(buffer, size, 1, f) != 1) | ||
713 | { | ||
714 | fclose(f); | ||
715 | printf("[ERR] Cannot write file '%s': %m\n", file); | ||
716 | return IMX_WRITE_ERROR; | ||
717 | } | ||
718 | fclose(f); | ||
719 | return IMX_SUCCESS; | ||
720 | } | ||
721 | |||
722 | /* compute MD5 sum of a buffer */ | ||
723 | static enum imx_error_t compute_md5sum_buf(void *buf, size_t sz, uint8_t file_md5sum[16]) | ||
724 | { | ||
725 | md5_context ctx; | ||
726 | md5_starts(&ctx); | ||
727 | md5_update(&ctx, buf, sz); | ||
728 | md5_finish(&ctx, file_md5sum); | ||
729 | return IMX_SUCCESS; | ||
730 | } | ||
731 | |||
732 | /* compute MD5 sum of a buffer */ | ||
733 | static enum imx_error_t compute_soft_md5sum_buf(struct sb_file_t *sb, uint8_t file_md5sum[16]) | ||
734 | { | ||
735 | md5_context ctx; | ||
736 | md5_starts(&ctx); | ||
737 | #define hash(obj) \ | ||
738 | md5_update(&ctx, (void *)&obj, sizeof(obj)) | ||
739 | /* various header fiels */ | ||
740 | hash(sb->timestamp); | ||
741 | hash(sb->drive_tag); | ||
742 | hash(sb->drive_tag); | ||
743 | hash(sb->first_boot_sec_id); | ||
744 | hash(sb->flags); | ||
745 | hash(sb->product_ver); | ||
746 | hash(sb->component_ver); | ||
747 | |||
748 | for(int i = 0; i < sb->nr_sections; i++) | ||
749 | { | ||
750 | struct sb_section_t *sec = &sb->sections[i]; | ||
751 | hash(sec->identifier); | ||
752 | uint32_t flags = sec->other_flags; | ||
753 | if(!sec->is_data) | ||
754 | flags |= SECTION_BOOTABLE; | ||
755 | if(sec->is_cleartext) | ||
756 | flags |= SECTION_CLEARTEXT; | ||
757 | hash(flags); | ||
758 | |||
759 | for(int j = 0; j < sec->nr_insts; j++) | ||
760 | { | ||
761 | struct sb_inst_t *inst = &sec->insts[j]; | ||
762 | switch(inst->inst) | ||
763 | { | ||
764 | case SB_INST_NOP: | ||
765 | /* ignore them totally because they are used for padding */ | ||
766 | break; | ||
767 | case SB_INST_LOAD: | ||
768 | hash(inst->inst); | ||
769 | hash(inst->addr); | ||
770 | md5_update(&ctx, inst->data, inst->size); | ||
771 | break; | ||
772 | case SB_INST_FILL: | ||
773 | hash(inst->inst); | ||
774 | hash(inst->addr); | ||
775 | hash(inst->pattern); | ||
776 | break; | ||
777 | case SB_INST_JUMP: | ||
778 | case SB_INST_CALL: | ||
779 | hash(inst->inst); | ||
780 | hash(inst->addr); | ||
781 | hash(inst->argument); | ||
782 | break; | ||
783 | case SB_INST_MODE: | ||
784 | hash(inst->inst); | ||
785 | hash(inst->argument); | ||
786 | break; | ||
787 | case SB_INST_DATA: | ||
788 | md5_update(&ctx, inst->data, inst->size); | ||
789 | break; | ||
790 | default: | ||
791 | printf("[ERR][INTERNAL] Unexpected instruction %d\n", inst->inst); | ||
792 | return IMX_ERROR; | ||
793 | } | ||
794 | } | ||
795 | } | ||
796 | #undef hash | ||
797 | md5_finish(&ctx, file_md5sum); | ||
798 | return IMX_SUCCESS; | ||
799 | } | ||
800 | |||
801 | /* compute MD5 of a file */ | ||
802 | enum imx_error_t compute_md5sum(const char *file, uint8_t file_md5sum[16]) | ||
803 | { | ||
804 | void *buf; | ||
805 | size_t sz; | ||
806 | enum imx_error_t err = read_file(file, &buf, &sz); | ||
807 | if(err != IMX_SUCCESS) | ||
808 | return err; | ||
809 | compute_md5sum_buf(buf, sz, file_md5sum); | ||
810 | free(buf); | ||
811 | return IMX_SUCCESS; | ||
812 | } | ||
813 | |||
814 | /* compute soft MD5 of a file */ | ||
815 | enum imx_error_t compute_soft_md5sum(const char *file, uint8_t soft_md5sum[16]) | ||
816 | { | ||
817 | clear_keys(); | ||
818 | add_key_list(list_all_keys); | ||
819 | /* read file */ | ||
820 | enum sb_error_t err; | ||
821 | struct sb_file_t *sb = sb_read_file(file, false, NULL, generic_std_printf, &err); | ||
822 | if(sb == NULL) | ||
823 | { | ||
824 | printf("[ERR] Cannot load SB file: %d\n", err); | ||
825 | return err; | ||
826 | } | ||
827 | /* compute sum */ | ||
828 | err = compute_soft_md5sum_buf(sb, soft_md5sum); | ||
829 | /* release file */ | ||
830 | sb_free(sb); | ||
831 | return err; | ||
832 | } | ||
833 | |||
834 | /* Load a rockbox firwmare from a buffer. Data is copied. Assume firmware is | ||
835 | * using our scramble format. */ | ||
836 | static enum imx_error_t rb_fw_load_buf_scramble(struct rb_fw_t *fw, uint8_t *buf, | ||
837 | size_t sz, enum imx_model_t model) | ||
838 | { | ||
839 | if(sz < 8) | ||
840 | { | ||
841 | printf("[ERR] Bootloader file is too small to be valid\n"); | ||
842 | return IMX_BOOT_INVALID; | ||
843 | } | ||
844 | /* check model name */ | ||
845 | uint8_t *name = buf + 4; | ||
846 | if(memcmp(name, imx_models[model].rb_model_name, 4) != 0) | ||
847 | { | ||
848 | printf("[ERR] Bootloader model doesn't match found model for input file\n"); | ||
849 | return IMX_BOOT_MISMATCH; | ||
850 | } | ||
851 | /* check checksum */ | ||
852 | uint32_t sum = imx_models[model].rb_model_num; | ||
853 | for(int i = 8; i < sz; i++) | ||
854 | sum += buf[i]; | ||
855 | if(sum != get_uint32be(buf)) | ||
856 | { | ||
857 | printf("[ERR] Bootloader checksum mismatch\n"); | ||
858 | return IMX_BOOT_CHECKSUM_ERROR; | ||
859 | } | ||
860 | /* two instructions: load and jump */ | ||
861 | fw->nr_insts = 2; | ||
862 | fw->entry_idx = 1; | ||
863 | fw->insts = xmalloc(fw->nr_insts * sizeof(struct sb_inst_t)); | ||
864 | memset(fw->insts, 0, fw->nr_insts * sizeof(struct sb_inst_t)); | ||
865 | fw->insts[0].inst = SB_INST_LOAD; | ||
866 | fw->insts[0].addr = imx_models[model].bootloader_addr; | ||
867 | fw->insts[0].size = sz - 8; | ||
868 | fw->insts[0].data = memdup(buf + 8, sz - 8); | ||
869 | fw->insts[1].inst = SB_INST_JUMP; | ||
870 | fw->insts[1].addr = imx_models[model].bootloader_addr; | ||
871 | return IMX_SUCCESS; | ||
872 | } | ||
873 | |||
874 | struct elf_user_t | ||
875 | { | ||
876 | void *buf; | ||
877 | size_t sz; | ||
878 | }; | ||
879 | |||
880 | static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) | ||
881 | { | ||
882 | struct elf_user_t *u = user; | ||
883 | if(addr + count <= u->sz) | ||
884 | { | ||
885 | memcpy(buf, u->buf + addr, count); | ||
886 | return true; | ||
887 | } | ||
888 | else | ||
889 | return false; | ||
890 | } | ||
891 | |||
892 | /* Load a rockbox firwmare from a buffer. Data is copied. Assume firmware is | ||
893 | * using ELF format. */ | ||
894 | static enum imx_error_t rb_fw_load_buf_elf(struct rb_fw_t *fw, uint8_t *buf, | ||
895 | size_t sz, enum imx_model_t model) | ||
896 | { | ||
897 | struct elf_params_t elf; | ||
898 | struct elf_user_t user; | ||
899 | user.buf = buf; | ||
900 | user.sz = sz; | ||
901 | elf_init(&elf); | ||
902 | if(!elf_read_file(&elf, elf_read, generic_std_printf, &user)) | ||
903 | { | ||
904 | elf_release(&elf); | ||
905 | printf("[ERR] Error parsing ELF file\n"); | ||
906 | return IMX_BOOT_INVALID; | ||
907 | } | ||
908 | fw->nr_insts = elf_get_nr_sections(&elf) + 1; | ||
909 | fw->insts = xmalloc(fw->nr_insts * sizeof(struct sb_inst_t)); | ||
910 | fw->entry_idx = fw->nr_insts - 1; | ||
911 | memset(fw->insts, 0, fw->nr_insts * sizeof(struct sb_inst_t)); | ||
912 | struct elf_section_t *sec = elf.first_section; | ||
913 | for(int i = 0; sec; i++, sec = sec->next) | ||
914 | { | ||
915 | fw->insts[i].addr = elf_translate_virtual_address(&elf, sec->addr); | ||
916 | fw->insts[i].size = sec->size; | ||
917 | if(sec->type == EST_LOAD) | ||
918 | { | ||
919 | fw->insts[i].inst = SB_INST_LOAD; | ||
920 | fw->insts[i].data = memdup(sec->section, sec->size); | ||
921 | } | ||
922 | else if(sec->type == EST_FILL) | ||
923 | { | ||
924 | fw->insts[i].inst = SB_INST_FILL; | ||
925 | fw->insts[i].pattern = sec->pattern; | ||
926 | } | ||
927 | else | ||
928 | { | ||
929 | printf("[WARN] Warning parsing ELF file: unsupported section type mapped to NOP!\n"); | ||
930 | fw->insts[i].inst = SB_INST_NOP; | ||
931 | } | ||
932 | } | ||
933 | fw->insts[fw->nr_insts - 1].inst = SB_INST_JUMP; | ||
934 | if(!elf_get_start_addr(&elf, &fw->insts[fw->nr_insts - 1].addr)) | ||
935 | { | ||
936 | elf_release(&elf); | ||
937 | printf("[ERROR] Error parsing ELF file: it has no entry point!\n"); | ||
938 | return IMX_BOOT_INVALID; | ||
939 | } | ||
940 | elf_release(&elf); | ||
941 | return IMX_SUCCESS; | ||
942 | } | ||
943 | |||
944 | /* Load a rockbox firwmare from a buffer. Data is copied. */ | ||
945 | static enum imx_error_t rb_fw_load_buf(struct rb_fw_t *fw, uint8_t *buf, | ||
946 | size_t sz, enum imx_model_t model) | ||
947 | { | ||
948 | /* detect file format */ | ||
949 | if(sz >= 4 && buf[0] == 0x7f && memcmp(buf + 1, "ELF", 3) == 0) | ||
950 | return rb_fw_load_buf_elf(fw, buf, sz, model); | ||
951 | else | ||
952 | return rb_fw_load_buf_scramble(fw, buf, sz, model); | ||
953 | } | ||
954 | |||
955 | /* load a rockbox firmware from a file. */ | ||
956 | static enum imx_error_t rb_fw_load(struct rb_fw_t *fw, const char *file, | ||
957 | enum imx_model_t model) | ||
958 | { | ||
959 | void *buf; | ||
960 | size_t sz; | ||
961 | int ret = read_file(file, &buf, &sz); | ||
962 | if(ret == IMX_SUCCESS) | ||
963 | { | ||
964 | ret = rb_fw_load_buf(fw, buf, sz, model); | ||
965 | free(buf); | ||
966 | } | ||
967 | return ret; | ||
968 | } | ||
969 | |||
970 | /* free rockbox firmware */ | ||
971 | static void rb_fw_free(struct rb_fw_t *fw) | ||
972 | { | ||
973 | for(int i = 0; i < fw->nr_insts; i++) | ||
974 | sb_free_instruction(fw->insts[i]); | ||
975 | free(fw->insts); | ||
976 | memset(fw, 0, sizeof(struct rb_fw_t)); | ||
977 | } | ||
978 | |||
979 | static bool contains_rockbox_bootloader(struct sb_file_t *sb_file) | ||
980 | { | ||
981 | for(int i = 0; i < sb_file->nr_sections; i++) | ||
982 | if(sb_file->sections[i].identifier == MAGIC_ROCK) | ||
983 | return true; | ||
984 | return false; | ||
985 | } | ||
986 | |||
987 | /* modify sb_file to produce requested boot image */ | ||
988 | static enum imx_error_t make_boot(struct sb_file_t *sb_file, const char *bootfile, | ||
989 | struct imx_option_t opt) | ||
990 | { | ||
991 | /* things went smoothly, we have a SB image but it may not be suitable as an | ||
992 | * input image: if it contains a rockbox bootloader, we need to remove it */ | ||
993 | if(contains_rockbox_bootloader(sb_file)) | ||
994 | { | ||
995 | printf("[INFO] SB file contains a Rockbox bootloader, trying to remove it...\n"); | ||
996 | enum imx_error_t ret = unpatch_firmware(opt, sb_file); | ||
997 | if(ret != IMX_SUCCESS) | ||
998 | return ret; | ||
999 | } | ||
1000 | /* if asked to produce OF, don't do anything more */ | ||
1001 | if(opt.output == IMX_ORIG_FW) | ||
1002 | return IMX_SUCCESS; | ||
1003 | /* load rockbox file */ | ||
1004 | struct rb_fw_t boot_fw; | ||
1005 | enum imx_error_t ret = rb_fw_load(&boot_fw, bootfile, opt.model); | ||
1006 | if(ret != IMX_SUCCESS) | ||
1007 | return ret; | ||
1008 | /* produce file */ | ||
1009 | ret = patch_firmware(opt, sb_file, boot_fw); | ||
1010 | rb_fw_free(&boot_fw); | ||
1011 | return ret; | ||
1012 | } | ||
1013 | |||
1014 | enum imx_error_t mkimxboot(const char *infile, const char *bootfile, | ||
1015 | const char *outfile, struct imx_option_t opt) | ||
1016 | { | ||
1017 | /* sanity check */ | ||
1018 | if(opt.fw_variant >= VARIANT_COUNT || opt.model >= MODEL_COUNT) | ||
1019 | return IMX_ERROR; | ||
1020 | /* dump tables */ | ||
1021 | dump_imx_dev_info("[INFO] "); | ||
1022 | /* load file */ | ||
1023 | void *buf; | ||
1024 | size_t offset = 0, size = 0; | ||
1025 | enum imx_error_t ret = read_file(infile, &buf, &size); | ||
1026 | if(ret != IMX_SUCCESS) | ||
1027 | return ret; | ||
1028 | /* compute MD5 sum of the file */ | ||
1029 | uint8_t file_md5sum[16]; | ||
1030 | compute_md5sum_buf(buf, size, file_md5sum); | ||
1031 | printf("[INFO] MD5 sum of the file: "); | ||
1032 | for(int i = 0; i < 16; i++) | ||
1033 | printf("%02x", file_md5sum[i]); | ||
1034 | printf("\n"); | ||
1035 | /* find model */ | ||
1036 | int md5_idx; | ||
1037 | ret = find_model_by_md5sum(file_md5sum, &md5_idx); | ||
1038 | /* is this a known firmware upgrade ? */ | ||
1039 | if(ret == IMX_SUCCESS) | ||
1040 | { | ||
1041 | enum imx_model_t model = imx_sums[md5_idx].model; | ||
1042 | printf("[INFO] File is for model %d (%s, version %s)\n", model, | ||
1043 | imx_models[model].model_name, imx_sums[md5_idx].version); | ||
1044 | /* check the model is the expected one */ | ||
1045 | if(opt.model == MODEL_UNKNOWN) | ||
1046 | opt.model = model; | ||
1047 | else if(opt.model != model) | ||
1048 | { | ||
1049 | printf("[ERR] Model mismatch, was expecting model %d (%s)\n", | ||
1050 | opt.model, imx_models[opt.model].model_name); | ||
1051 | free(buf); | ||
1052 | return IMX_MODEL_MISMATCH; | ||
1053 | } | ||
1054 | /* use database values */ | ||
1055 | offset = imx_sums[md5_idx].fw_variants[opt.fw_variant].offset; | ||
1056 | size = imx_sums[md5_idx].fw_variants[opt.fw_variant].size; | ||
1057 | if(size == 0) | ||
1058 | { | ||
1059 | printf("[ERR] Input file does not contain variant '%s'\n", imx_fw_variant[opt.fw_variant]); | ||
1060 | free(buf); | ||
1061 | return IMX_VARIANT_MISMATCH; | ||
1062 | } | ||
1063 | /* special case: if we need to produce the OF, just bypass read/write of | ||
1064 | * the SB file and output this chunk of the file. This is faster and it | ||
1065 | * also avoids modifying the OF by reconstructing it */ | ||
1066 | if(opt.output == IMX_ORIG_FW) | ||
1067 | { | ||
1068 | printf("[INFO] Extracting original firmware...\n"); | ||
1069 | ret = write_file(outfile, buf + offset, size); | ||
1070 | free(buf); | ||
1071 | return ret; | ||
1072 | } | ||
1073 | } | ||
1074 | else | ||
1075 | { | ||
1076 | printf("[INFO] File doesn't have a known MD5 sum, assuming it's a SB image...\n"); | ||
1077 | /* image didn't match, so we expect the file to be a raw SB image, either | ||
1078 | * produced by mkimxboot when uninstalling bootloader or after installing RB, | ||
1079 | * so load all known keys and go on */ | ||
1080 | |||
1081 | /* To be more user friendly, give a nice error message if we detect | ||
1082 | * the file is not a SB file */ | ||
1083 | if(guess_sb_version(infile) == SB_VERSION_UNK) | ||
1084 | { | ||
1085 | printf("[ERR] Your firmware doesn't look like a SB file\n"); | ||
1086 | printf("[ERR] This is probably a firmware upgrade\n"); | ||
1087 | printf("[ERR] Unfortunately, this tool doesn't know about it yet\n"); | ||
1088 | printf("[ERR] Please report to the developers to add it\n"); | ||
1089 | free(buf); | ||
1090 | return IMX_ERROR; | ||
1091 | } | ||
1092 | } | ||
1093 | /* to proceed further, we need to know the model */ | ||
1094 | if(opt.model == MODEL_UNKNOWN) | ||
1095 | { | ||
1096 | printf("[ERR] Cannot do processing of soft image without knowing the model\n"); | ||
1097 | free(buf); | ||
1098 | return IMX_MODEL_MISMATCH; | ||
1099 | } | ||
1100 | /* load image */ | ||
1101 | g_debug = opt.debug; | ||
1102 | clear_keys(); | ||
1103 | add_key_list(imx_models[opt.model].keys); | ||
1104 | enum sb_error_t err; | ||
1105 | struct sb_file_t *sb_file = sb_read_memory(buf + offset, size, false, NULL, generic_std_printf, &err); | ||
1106 | if(sb_file == NULL) | ||
1107 | { | ||
1108 | printf("[ERR] Cannot open firmware as SB file: %d\n", err); | ||
1109 | free(buf); | ||
1110 | return IMX_FIRST_SB_ERROR + err; | ||
1111 | } | ||
1112 | /* modify image */ | ||
1113 | ret = make_boot(sb_file, bootfile, opt); | ||
1114 | if(ret == IMX_SUCCESS) | ||
1115 | { | ||
1116 | /* write image */ | ||
1117 | ret = sb_write_file(sb_file, outfile, NULL, generic_std_printf); | ||
1118 | } | ||
1119 | /* cleanup */ | ||
1120 | sb_free(sb_file); | ||
1121 | free(buf); | ||
1122 | return ret; | ||
1123 | } | ||